イレイジャってなんじゃ?
他の方のレベルが高く、場違いじゃないかと本心はキョドっていますが、そんなことは気にせず、ハードルをくぐるスタイルで進めていきます。あしからず。
この記事はなんじゃ?(概要)
みなさん、Java のイレイジャ(Type Erasure)はご存知ですか?
少し前までの私はよく知りませんでした。
この記事では、イレイジャとは何かを自分なりに学ぶための記事になります。
実装ですぐに役に立つものではないですが、Java について興味を持ってもらえればと思います。
前日譚(そもそものきっかけ)
そもそも、どこでイレイジャと出会ったのか?
それは、今年の10月に開かれた JJUG CCC 2024 Fall のなぎせさんセッションJava ジェネリクス入門 2024です。
正直、恥ずかしながら自分はこのセッションを聞くまでは知らなかったです。
イレイジャ自体は実装やコードを書くうえで意識することはあまりないですが、改めてどういうものか知っておきたいと思い、記事にしました。
(少し内容が薄いのはご容赦ください。。。)
本題
イレイジャとは何なのか?
Java Language Specification では、以下のように記載されています。
Type erasure is a mapping from types (possibly including parameterized types and type variables) to types (that are never parameterized types or type variables).
簡単に和訳すると、
Type Erasure はパラメータ化された型や型変数を含んだ型からそれらを含まない型への写像である。
具体的な写像の定義は上記の JLS にも書いているとおり、以下のルールを満たすものとなっています。
- パラメータ化された型
G<T1,...,Tn>
の型消去は|G|
です - ネストされた型
T.C
の型消去は|T|.C
です - 配列型
T[]
の型消去は|T|[]
です - 型変数の型消去は、その最左の境界の型消去と同じです
- その他すべての型の型消去は、その型自体です
いつどこで使われるのか?
イレイジャはコンパイルのタイミングに発揮されます。
簡単な例で考えてみます。
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
System.out.println(list.get(0));
}
}
この Java ファイルをコンパイルしてできた Class ファイルをデコンパイルしたイメージが下記になります。[1]
public class Main {
public static void main(String[] var0) {
ArrayList var1 = new ArrayList();
var1.add("A");
var1.add("B");
System.out.println((String)var1.get(0));
}
}
比較すると、それぞれの 3 行目でリストを生成するときの型が List<String>
から ArrayList
になっており、<String>
に関する情報がなくなっていることがわかります。
加えて、new
で生成している箇所も ArrayList<>()
から ArrayList()
になっており、こちらも <>
の情報がなくなっていることがわかります。
つまり、先程書いたイレイジャの定義の通り、コンパイル後のクラスファイル内で型の写像として変換されていることがわかります。
なぜ必要なのか?
これは、ジェネリクスが導入されたタイミングで後方互換性とパフォーマンス問題への対処のためになります。[2]
さすがに、ジェネリクスが導入された Java 5(J2SE 5.0)より前(つまり、1.4 以前)のソースが存在することは考えにくい気がしますが、Java の後方互換性が保っているのは個人的に好きです。
さいごに
イレイジャ自体は冒頭にも書いた通り、普段の業務やコードを書くにあたっては意識する機会は少ないと思います。
なので、この記事をきっかけに、Java における型やクラス、コンパイラーなど言語仕様に関しても興味を持ってもらえれば幸いです。
参考
- Java Language Specification - Type Erasure
- Baeldung - Type Erasure in Java Explained
- Baeldung - The Basics of Java Generics
-
クラスファイルのデコンパイルには、IntelliJ IDEA に備わっているデコンパイラを使用しました。(FernFlower decompiler) ↩︎
-
他にも、型安全性の低下やシグネチャの競合などの理由もあります。 ↩︎
Discussion