Scala におけるプリミティブ型の扱い
Scala は純粋オブジェクト指向言語であり、Java にあるプリミティブ型は存在しません。Java の int などはScala では Int というオブジェクトとして扱われます。しかしこれはコンパイル段階の話で、出力されるバイトコードでは、Scala コンパイラによる最適化が行われ、オブジェクトは対応するプリミティブ型に変換されます。
型消去(type erasure)
Scala の型パラメータは Java のジェネリクスと同様に type erasure(型消去) となります。型パラメータの情報はコンパイルの段階で検査され、コンパイル後のバイトコードからは消去されます。コンパイル後、型パラメータは上限境界(デフォルトではObject)型に変換されます。
型パラメータが消去された結果、プリミティブ値をオブジェクトとして扱うためにボクシングとアンボクシングの処理を追加する必要があります。この処理は、最初からプリミティブ型を扱うように最適化したコードと比較して10倍ほど低速になります。そのため、ジエネリクスなコレクションの使用を避け、プリミティブ型に特化したコードを書くプログラマもいるほどです。
特化型パラメータ(Specialized type parameter)
Scala 2.8 は特化型パラメータ(Specialized type parameter)が追加されました。特化型パラメータはコンパイラに、ジェネリクス版とナンバー型に特化した版の 2 つの定義を生成します。そして、可能な限り特化した版の定義を使用するようにします。特化型パラメータは、型パラメータに @specialized アノテーションを指定することでコンパイラに指示します。
特化型パラメータの文法
メソッドとクラスのどのようなパラメータ型も @specialized アノテーションを付けることができます。クラスの型パラメータとして指定するには以下のようにします。
class Vector[@specialized A] {}
このクラスは、型パラメータとして従来通りのものと、数値である全てのプリミティブ型に特化したものの 2 つの処理を個別に扱うようになります。
対象とするプリミティブ型を指定することもできます。
def map[@specialized(Int, Boolean) B](f: A => B) =
このメソッドは integers と booleans に対してのみ特化型パラメータとして扱われるようになります。
特化型パラメータのしくみ
型パラメータ T を使用する RefCell を考えます。T を Int 限定の特化型パラメータとして定義します。
class RefCell[@specialized(Int) T] { private var value: T = _ def get: T = x def put(x: T) = value = x }
上記コードは Scala コンパイラにより以下のように扱われます。
class RefCell$mcI$sp extends RefCell/*[Int]*/ { protected override def value: AnyRef = value$mcI$ // ボクシング発生 override def get: AnyRef = get$mcI$sp; // ボクシング発生 override def put(x: AnyRef): Unit = put$mcI$sp(x); // ボクシング発生 protected var value$mcI$sp: Int = _; protected override def value_=(x$1: Int): Unit = value$mcI$sp = x$1 override def get$mcI$sp: Int = value$mcI$sp; override def put$mcI$sp(x: Int): Unit = value$mcI$sp = x }
上部の AnyRef を型パラメータとして使用する部分に加え、下部の Int に特化した部分が定義されています。
以下のようなクライアントコードでは Int に特化した定義(RefCell$mcI$sp)が使用されます。Int を扱うコードはコンパイラによりプリミティブ型として扱われるため、ボクシング処理によるパフォーマンスの低下を防ぐことができるようになります。
object Test extends Application { val ref = new RefCell[Int] ref.put(10) }