从Any到AnyRef的通用/通用拳击

时间:2014-07-15 11:27:41

标签: scala boxing

是否可以动态强制在scala中运行时装箱?我想要一个功能:

    def box(value :Any) :AnyRef

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

我有一个泛型类,可以使用AnyVals进行参数化,但需要将它们传递给接受对象集合的旧Java方法。当然,我可以自己使用模式匹配来实现它,但是必须一次又一次地执行它有点烦人,并且它不适用于用户值类。

修改

答案简直令人惊讶。现在,我可以通过反思来做到吗?假设

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

我想做一个安全集,检查运行时是否o是T的子类,如下所示:

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

不幸的是,Box [Long]的typeOf [T]将是一个原语,并且java.lang.Long上的以下检查将失败,例如,Seq [Long]的元素的运行时类型。最后,当使用泛型与AnyVals时,编译器有时会将它们设置为使运行时类检查不可预测。

1 个答案:

答案 0 :(得分:4)

使用AnyRef转换为asInstanceOf,Scala会将其转换为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

对于值类,这将把它放在值类的实例中,而不是Java类。您可以使用通用特征来从您的值类中获取Java盒装版本,而无需使用反射。例如:

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
}

但是,这需要您将特征混合到所有值类中。对于扩展Product的值类,与所有案例类一样,使用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
}

不幸的是,它看起来像我这两种方法(混合特性和Product)导致Scala进行拳击,这意味着当在未装箱的值上调用时,值来自未装箱→Scala装箱→未装箱→ Java装箱,而不是直接使用Java盒装值。