Java ジェネリクス でつまづいた話

javaのジェネリクスで、
なぜかコンパイルエラーになってくれないものがあったので記載しておく。

ジェネリクスのコンパイル後のクラスファイルについて

@SuppressWarnings("unchecked")
static <T extends Number> T get()
{
    return (T) obj;
}

↓ コンパイルされると、型情報が消えるので、クラスファイルはこうなる。

@SuppressWarnings("unchecked")
static Number get()
{
    return (Number) obj;
}

コンパイルエラーになりそうで、ならないもの。

Integer i = get();            // OK
Double  d = get();            // OK
String  s = get();            // コンパイルエラー
List<String> strList = get(); // コンパイルエラーになってくれない

↓ コンパイルされると、こう展開される。
型情報を元に自動でキャストを挟んでいるようなもの。

Integer i = (Integer) get();                 // OK
Double  d = (Double) get();                  // OK
String  s = (String) get();                  // コンパイルエラー
List<String> strList = (List<String>) get(); // コンパイルエラーになってくれない

一番したのやつは、明らかにエラーなんだけど、
なぜコンパイル時エラーが発生しないのか??

上記は、これと同等のキャスト

Number n = 1;

Integer i = (Integer) n;                 // OK
Double  d = (Double) n;                  // OK
String  s = (String) n;                  // コンパイルエラー
List<String> strList = (List<String>) n; // コンパイルエラーになってくれない

もともと、右辺がジェネリクスの場合

右辺がもともと、List<>, Set<> などのジェネリクス型の場合は、
ジェネリクス型以外のキャストはエラーになるみたいです。
あと、型パラメータのチェックもされるみたいです。

List<? extends Number> numList = new ArrayList<>();

List<Integer> intList = (List<Integer>) numList; // OK
Set<Double> doubleSet = (Set<Double>) numList;   // これもコンパイルエラーにならない

Integer i = (Integer) numList;                 // コンパイルエラー
String  s = (String) numList;                  // コンパイルエラー
List<String> strList = (List<String>) numList; // コンパイルエラー

おしまい

これなんで、コンパイルエラーになってくれないんだろう〜??

ジェネリクス型へのキャストの際は、原型自体のチェックはされなくて、
型パラメータのチェックだけされる。
右辺に型パラメータがないから、左辺の型パラメータともチェックできない。
という事なのかな??

だれか、わかる人教えてほしい…。

コメントを残す