2014-07-15 14 views
5

E 'possibile forzare la boxe di runtime in scala dinamicamente? Vorrei una funzione:Boxe universale/generico da Any a AnyRef

def box(value :Any) :AnyRef 

o

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

ho una classe generica che può essere parametrizzato con AnyVals, ma ha bisogno di passarli ai metodi eredità java accettare collezioni di oggetti. Naturalmente, potrei implementarlo io stesso con il pattern matching, ma è un po 'fastidioso doverlo fare più e più volte, e non funzionerebbe per le classi di valore utente.

Modifica

La risposta si è rivelata tanto semplice quanto sorprendente. Ora, posso farlo attraverso la riflessione? Assumere

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

vorrei fare una serie di sicurezza, il check-nel runtime se o è una sottoclasse di T come questo:

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

Purtroppo, TypeOf [T] per Box [fino a quando] essere un primitivo, e il seguente controllo fallirà su java.lang.Long, che è il tipo di runtime di elementi di Seq [Long] ad esempio. In conclusione, quando si usano i generici con AnyVals, il compilatore a volte li inserisce rendendo i controlli della classe di runtime imprevedibili.

+0

La parte aggiunta non sembra riguardare il boxing da "Qualsiasi" a "AnyRef". (Sembra che abbia più a che fare con l'unboxing?) Che ne dici di renderlo una seconda domanda separata? –

risposta

4

appena lanciato a AnyRef con asInstanceOf e Scala si trasformerà in 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 

Per le classi di valore, questo sarà casella in un'istanza della classe di valore, al posto della classe Java. È possibile utilizzare un tratto generico per essere in grado di ottenere le versioni in scatola Java dalle classi di valore, senza utilizzare la reflection. Ad esempio:

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 
} 

Tuttavia, questo richiede di mescolare il tratto in tutte le classi di valore. Per le classi di valori che si estendono Product, come tutte le classi case fanno, c'è un modo più rapido utilizzando 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 
} 

Purtroppo, sembra a me come entrambi i metodi (trait mix-in e Product) causa Scala a fare la sua boxe , ovvero quando viene chiamato su un valore non inserito, il valore passa da unboxed → Scala in box → unboxed → Java in box, invece che direttamente al valore in box Java.

+0

Fantastico, grazie! È piuttosto divertente però, dato che '42.asInstanceOf [AnyVal]' produrrà un avvertimento del compilatore che dice che è sempre falso, ma valuterà come 'true'. Inoltre, non sembra funzionare per le classi di valore utente. Ho aggiunto una domanda bonus alla domanda originale per ulteriore credito. – Turin

Problemi correlati