この質問に対する受け入れられた最良の回答には、ボクシングが発生する理由が明確に説明されています。
ただし、(java decompilerを使用して)コードを逆コンパイルすると、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
は、プリミティブ型のボクシング メソッドを含むオブジェクトであるため、合計で 1 つのインスタンスが存在する必要があります。ここでの秘訣は、ライブラリ内のこの特定のファイルが Java で記述されており、変換が静的メソッドであることです。このため、実行時にインスタンスはありませんが、Scala コードで使用するとオブジェクトのように感じられます。
おそらく、JProfile でボックス化されたプリミティブ (java.lang.Integer など) を探す必要がありますが、JVM がどのように機能するか、実行時に実際にコードを書き換えて最適化してボックス化を回避できるかどうかはわかりません。私の知る限りでは、専門化を適用するべきではありません (ただし、CLR は適用すると思います)。ボクシングの状況がある場合とない場合のいくつかのマイクロベンチマークは、実行時に何が起こるかを把握する別の方法です。
編集:
上記は、型パラメーターに注釈が付けられていないことを前提としています@specialized
。この場合、ボックス化/ボックス化解除を回避できます。標準ライブラリの特定のクラスは特殊化されています。この sidを参照してください。
次のTest.scalaプログラムがあるとします。
object Test {
def main(args:Array[String]) {
val list = List(1,5,15)
val res = list.map(e => e*2).filter(e => e>10)
}
}
でコンパイルするとscalac -Xprint:jvm Test.scala
、特殊化が発生することを示唆するこのスニペットが表示されます(ワイドペーストで申し訳ありません):
package <empty> {
final class Test extends java.lang.Object with ScalaObject {
def main(args: Array[java.lang.String]): Unit = {
val list: List = immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 5, 15}));
val res: List = list.map({
(new Test$$anonfun$1(): Function1)
}, immutable.this.List.canBuildFrom()).$asInstanceOf[scala.collection.TraversableLike]().filter({
(new Test$$anonfun$2(): Function1)
}).$asInstanceOf[List]();
()
};
def this(): object Test = {
Test.super.this();
()
}
};
@SerialVersionUID(0) @serializable final <synthetic> class Test$$anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp {
final def apply(e: Int): Int = Test$$anonfun$1.this.apply$mcII$sp(e);
<specialized> def apply$mcII$sp(v1: Int): Int = v1.*(2);
final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Int.box(Test$$anonfun$1.this.apply(scala.Int.unbox(v1)));
def this(): Test$$anonfun$1 = {
Test$$anonfun$1.super.this();
()
}
};
@SerialVersionUID(0) @serializable final <synthetic> class Test$$anonfun$2 extends scala.runtime.AbstractFunction1$mcZI$sp {
final def apply(e: Int): Boolean = Test$$anonfun$2.this.apply$mcZI$sp(e);
<specialized> def apply$mcZI$sp(v1: Int): Boolean = v1.>(10);
final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Boolean.box(Test$$anonfun$2.this.apply(scala.Int.unbox(v1)));
def this(): Test$$anonfun$2 = {
Test$$anonfun$2.super.this();
()
}
}
}
バイトコードにボクシングの証拠が表示されないのはそのためかもしれません...