Javaジェネリクスの実装方式(型消去)

Javaはジェネリクス(総称型)を型消去(type erasure)という方式で実装している。コンパイル時に型パラメータを取り除いてしまうため、List<Integer>List<String>は実行時にはまったく同一のバイトコードになる。ジェネリクスの実装方式という観点では、型ごとにコードを複製する単態化とも、型情報を実行時に辞書として渡す辞書渡しとも異なる第3の道で、型情報そのものを消してしまう点が特徴。

コンパイラが行う変換

  1. 型パラメータを境界型で置き換える。境界のない<T>Objectに、<T extends Number>のように上限境界があればその境界型に置き換える。
  2. 型安全性を保つため、必要な箇所にキャストを挿入する。
  3. ポリモーフィズムを保つため、ブリッジメソッド(bridge method)を生成する。

プリミティブ型はジェネリクスの型引数に取れないため、intIntegerにボクシングされる。生成されたバイトコードにはジェネリクスの型情報が残らないため、リフレクションで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年分の既存バイトコードとの互換性維持が難しく、可能性は低いとされる。

出典

#java #generics #compiler-design

作成日時: 2026-08-15 21:56 / 更新日時: 2026-08-15 21:56