在对this question的公认最佳答复中,有一个清楚的解释为何发生装箱。
但是,如果我反编译代码(使用java反编译器),则看不到使用scala.runtime.BoxesRunTime。此外,如果我分析了代码(使用JProfiler),则看不到BoxesRunTime的任何实例。
那么,我如何真正看到装箱/拆箱的证据?
最佳答案
在此代码中:
class Foo[T] {
def bar(i: T) = i
}
object Main {
def main(args: Array[String]) {
val f = new Foo[Int]
f.bar(5)
}
}
bar
的调用应首先将整数装箱。使用Scala 2.8.1编译并使用:javap -c -l -private -verbose -classpath <dir> Main$
查看为
main
类的Main
方法生成的字节码会产生:public void main(java.lang.String[]);
...
9: iconst_5
10: invokestatic #24; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
13: invokevirtual #28; //Method Foo.bar:(Ljava/lang/Object;)Ljava/lang/Object;
16: pop
17: return
...
您可以在调用
BoxesRunTime
之前看到对bar
的调用。BoxesRunTime
是一个对象,其中包含用于原始类型的装箱方法,因此总共应该恰好有一个实例。这里的技巧是库中的这个特定文件是用Java编写的,并且转换是静态方法。因此,在运行时没有任何实例,尽管在Scala代码中使用它似乎像是一个对象。尽管我不确定JVM的工作方式以及它是否可能在运行时实际重写代码并优化以避免装箱,但您可能应该使用JProfile查找装箱的原语(例如java.lang.Integer)。据我所知,它不应该应用专业化(但我相信CLR可以)。带有和不带有拳击情况的一些微基准测试是弄清运行时会发生什么情况的另一种方法。
编辑:
上面假设类型参数未使用
@specialized
注释进行注释。在这种情况下,可以避免装箱/拆箱。标准库中的某些类是专门的。请参见this sid。