LoginSignup
2
1

不思議なコード ~java ジェネリクス型の型消去~

Last updated at Posted at 2023-07-22

この記事の目的

簡単なコード例から、javaのジェネリクス型における型消去(Type Erasure)という仕組みを見ていきます。

ちょっと変なコードがある→面白そう と感じてもらえればうれしいです。

この記事では、型消去は簡単な解説にとどめます。
詳しい説明は、他の記事や公式ドキュメントに譲ります。
 参考:java 公式ドキュメント(Type Erasure)
 https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

まずはコード

Container.java
public class Container<V>{
    V value;
    public void setValue(Object value){
        this.value=(V)value;
    }
}
Main.java
public class Main{
    public static void main(String... arg){
        Container<String> con=new Container<>();
        Integer i=0;
        con.setValue(i);
    }
}

おかしなコードですね

まず、setValue(value)の引数型がObjectです。ここは、Vを引数の型にしたほうがよいですが、コンパイルは通りそうです。

つぎに、con.setValue(i)の部分を見てください。Stringvalueとして持つconInteger型の変数iを入れようとしていてまずいですね。

では、コンパイルが通るか・実行できるかという観点ではどうなのでしょうか?

このコードは実行できるのか?

次の二つの観点で見ると、このコードはどうでしょうか。

  • コンパイルは通りますか?
  • それとも実行時エラーが投げられる?(ClassCastException?)

答えは...













実は正常に動きます

実は、全く問題なく動きます。
※コンパイル・実行はできますが、this.value=(V)value;の部分でwarningは出ます。

例えば、次のようなprint()を実装しても、ちゃんと動きます:

Container.java
//Containerクラスの中
    public void print(){
        System.out.println("value is "+this.value);
    }
con.print()の実行結果例
//con.setValue(0)の場合
value is 0

//con.setValue("tmp")の場合
value is tmp

仕組みを解説

タイトルの通り、型消去(Type Erasure)という仕組みが肝です。
これは、javaにおけるジェネリクス型における仕組みです。

よくあるジェネリクス型の説明

ジェネリクス型によって、
Container<Object> Container<Integer> Container<String>
のように、複数のクラスがコンパイラで作られる

という説明がよくあります。
が、これは正確には正しくありません

実際には

コンパイルの時点で型の情報は消え、実際には
Container<Object>
というクラスが使いまわされます。

これを型消去Type Erasure)といいます。

つまり、

Container<String> conという変数は、コンパイル後(実行時)にはContainer<Object>となっているわけです。

最初のコードを見返すと

前述の通りコンパイルは通ります。つまり、conContainer<Object>の変数として扱われます。

最初のコードは、次のように解釈できます。

Container<String> con=new Container<>();
//実行時には、Container<Object>として扱われる
//con.valueの型もObject

Integer i=0;
con.setValue(i);
//setValue(i)の中では
//con.value=(Object)value; が実行される

実行時には、コメントで書いたような処理になります。

型消去の仕組みのせいで、最初に示したコードも普通に動くのです。

まとめ

本記事では、型消去(Type Erasure)の次の2つ特徴を利用しました。

  • コンパイル後に型の情報は消える
  • Container<Object>というクラスが使いまわされる

これによって、最初のコードは動くわけです。

ちょっとでも面白いな、不思議だな、と思っていただけましたか?

参考

java 公式ドキュメント
https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

改訂2版 パーフェクトJava 井上 誠一郎・永井 雅人 (著) 技術評論社

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1