Javaジェネリクスの実装方式(型消去)
Javaはジェネリクス(総称型)を型消去(type erasure)という方式で実装している。コンパイル時に型パラメータを取り除いてしまうため、List<Integer>とList<String>は実行時にはまったく同一のバイトコードになる。ジェネリクスの実装方式という観点では、型ごとにコードを複製する単態化とも、型情報を実行時に辞書として渡す辞書渡しとも異なる第3の道で、型情報そのものを消してしまう点が特徴。
コンパイラが行う変換
- 型パラメータを境界型で置き換える。境界のない
<T>はObjectに、<T extends Number>のように上限境界があればその境界型に置き換える。 - 型安全性を保つため、必要な箇所にキャストを挿入する。
- ポリモーフィズムを保つため、ブリッジメソッド(bridge method)を生成する。
プリミティブ型はジェネリクスの型引数に取れないため、intはIntegerにボクシングされる。生成されたバイトコードにはジェネリクスの型情報が残らないため、リフレクションでList<Integer>からIntegerを取得することはできない。
採用理由: 後方互換性
Java 5でジェネリクス導入にあたり、既存の何百万行ものコード・バイナリとの互換性を壊さない方式として選ばれた妥協案。型消去によって新しいクラスが一切生成されないため、ジェネリクス導入によるランタイムオーバーヘッドは(ボクシングを除けば)実質ゼロという利点もある。
Russ Coxによる評価
The Generic Dilemma(2009)は、Javaのこのアプローチを「暗黙のボクシングによる実行時オーバーヘッド」の実例として名指ししている。単態化(C++)・型消去+ボクシング(Java)という当時の2つの代表例を並べ、両方の欠点を避ける第3の道があるかを問いかけた記事。
将来: Project Valhallaによる特化
Project Valhallaでは値型(value type)の導入に合わせて、コレクションをプリミティブ値向けに特化させるgeneric specializationが検討されている。ただし参照型ジェネリクスへの完全な reified generics(実行時に型情報を保持する方式)の導入は、20年分の既存バイトコードとの互換性維持が難しく、可能性は低いとされる。
出典
- Type Erasure - Dev.java
- Java’s Type Erasure: The Generics Compromise That Haunts Us Today - Java Code Geeks
- Project Valhalla (Java language) - Wikipedia)
- Hacker News: reified generics discussion
#java #generics #compiler-design