Is it possible to force runtime boxing in scala dynamically? I would like a function:
def box(value :Any) :AnyRef
or
def box[T](value :T) :AnyRef
I have a generic class which may be parameterized with AnyVals but need to pass them to legacy java methods accepting collections of objects. Of course, I could implement it myself with pattern matching, but it's a bit annoying to have to do it time and time again, and it wouldn't work for user value classes.
Edit
The answer turned out as simple as surprising. Now, can I do it through reflection? Assume
class Box[T :TypeTag](private var value :T) {
def get :T = value
def set(o :Any) {
...
}
}
I would like to do a safe set, checking in the runtime if o is a subclass of T like this:
runtimeMirror(getClass.getClassLoader).classSymbol(o.getClass).toType <:< typeOf[T]
Unfortunately, typeOf[T] for Box[Long] will be a primitive, and the following check will fail on java.lang.Long, which is the runtime type of elements of Seq[Long] for example. Bottom line, when using generics with AnyVals, the compiler sometimes boxes them making runtime class checks unpredictible.
Just cast to AnyRef
with asInstanceOf
and Scala will turn it into an 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
For value classes, this will box it in an instance of the value class, instead of the Java class. You could use a generic trait to be able to get the Java boxed versions from your value classes, without using reflection. For example:
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
}
However, this requires you mix your trait into all your value classes. For value classes that extend Product
, like all case classes do, there's a quicker way using 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
}
Unfortunately, it looks to me like both methods (mix-in trait and Product
) cause Scala to do its boxing, meaning when called on an unboxed value, the value goes from unboxed → Scala boxed → unboxed → Java boxed, instead of straight to the Java boxed value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With