2014-07-15 4 views
5

Est-il possible de forcer la boxe d'exécution dans Scala dynamiquement? Je voudrais une fonction:Boxing universel/générique de Any à AnyRef

def box(value :Any) :AnyRef 

ou

def box[T](value :T) :AnyRef 

J'ai une classe générique qui peut être paramétrés avec AnyVals mais il faut les passer à des méthodes Java existants qui acceptent des collections d'objets. Bien sûr, je pourrais l'implémenter moi-même avec la correspondance de modèle, mais c'est un peu ennuyeux d'avoir à le faire encore et encore, et cela ne fonctionnerait pas pour les classes de valeur utilisateur.

Modifier

La réponse est avéré aussi simple que surprenante. Maintenant, puis-je le faire par réflexion? Supposons

class Box[T :TypeTag](private var value :T) { 
    def get :T = value 
    def set(o :Any) { 
     ... 
    } 
} 

Je voudrais faire un ensemble sûr, la vérification du moteur d'exécution si o est une sous-classe de T comme ceci:

runtimeMirror(getClass.getClassLoader).classSymbol(o.getClass).toType <:< typeOf[T] 

Malheureusement, typeof [T] pour Box [Long] sera être une primitive, et la vérification suivante échouera sur java.lang.Long, qui est le type d'éléments d'exécution de Seq [Long] par exemple. Bottom line, lors de l'utilisation de génériques avec AnyVals, le compilateur les encadre parfois rendant les contrôles de classe d'exécution imprévisibles.

+0

Votre partie ajoutée ne semble pas concerner la boxe de 'Any' à' AnyRef'. (On dirait que cela a plus à voir avec le déballage?) Que diriez-vous d'en faire une deuxième question séparée? –

Répondre

4

Juste jeté à AnyRef avec asInstanceOf et Scala se transformer en un AnyRef:

scala> val x = 13 
x: Int = 13 

scala> val xBoxed = x.asInstanceOf[AnyRef] 
xBoxed: AnyRef = 13 

scala> xBoxed.getClass() 
res0: Class[_ <: AnyRef] = class java.lang.Integer 

Pour les classes de valeur, ce sera elle boîte dans une instance de la classe de valeur, au lieu de la classe Java. Vous pouvez utiliser un trait générique pour obtenir les versions en boîte Java de vos classes de valeurs, sans utiliser de réflexion. Par exemple:

trait ValueClass[T] extends Any { 
    def value: T 
    def javaBoxed: AnyRef = value.asInstanceOf[AnyRef] 
} 

case class MyInt(value: Int) extends AnyVal with ValueClass[Int] 

case class MyReal(asDouble: Double) extends AnyVal with ValueClass[Double] { 
    def value = asDouble 
} 

Toutefois, cela nécessite de mélanger votre trait dans toutes vos classes de valeur. Pour les classes de valeur qui se prolongent Product, comme toutes les classes de cas font, il y a un moyen plus rapide à l'aide productElement:

def javaBox(x: Product): AnyRef = { 
    if (x.productArity == 1) x.productElement(0).asInstanceOf[AnyRef] 
    else x.asInstanceOf[AnyRef] // or throw an exception if you prefer 
} 

Malheureusement, il semble à moi comme les deux méthodes (trait mix-et Product) la cause Scala de faire sa boxe , ce qui signifie que lorsqu'il est appelé sur une valeur sans boîte, la valeur va de boîte → Scala encadré → boîte → Java en boîte, au lieu de directement à la valeur encadrée Java.

+0

Génial, merci! Il est assez amusant, car '42.asInstanceOf [AnyVal]' produira un avertissement du compilateur en disant que c'est toujours faux, mais qu'il va évaluer 'true'. En outre, il ne semble pas fonctionner pour les classes de valeur utilisateur. J'ai ajouté une question bonus à la question initiale pour un crédit supplémentaire. – Turin

Questions connexes