Genericsの適用・非適用
Generics絡みで同僚に質問されたが即答できず、悔しかったので調査。質問の主旨は以下の通り。
戻り値の型をパラメータ化しているが、なぜかキャストしないとコンパイルエラーになってしまうのはなぜか?
これは元々、以下のエントリを見て??となったのが原因だったのです。
clazzパラメータの型をClassにするとキャストする必要はありません。こうかけます。
private String findTableNameFromModelAnnotation(Class<?> clazz) { ... Table annotaion = clazz.getAnnotation(Table.class);Java5以降は、Classの原型を使う意味は余りないと思いますよ。Eclipseを使っているなら、Classの原型を使ったときに、警告を出すように設定できるので、Eclipseにチェックしてもらったほうがいいです。
2008-10-30 - yvsu pron. yas
引数に "Class clazz" と書くとなぜOKで、"Class clazz" だとなぜダメなのだろうか…。
Class#getAnnotaionの定義を見ると以下の通りで、Classクラス自体のパラメータであるTとはなにも関係なく、一見特に問題なさそうな気がします。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
問題を単純化するために以下のクラスを書いてみました。
public class Test { interface Animal { void turn(); } interface Monkey extends Animal { } interface Zoo<E> { public <A extends Animal, T> A getAnimal(List<T> list); } static void show1(Zoo zoo, List favorite) { Monkey monkey = zoo.getAnimal(favorite); // コンパイルエラー monkey.turn(); } static void show2(Zoo<?> zoo, List favorite) { Monkey monkey = zoo.getAnimal(favorite); // コンパイルエラー monkey.turn(); } static void show3(Zoo zoo, List<?> favorite) { Monkey monkey = zoo.getAnimal(favorite); // コンパイルエラー monkey.turn(); } static void show4(Zoo<?> zoo, List<?> favorite) { Monkey monkey = zoo.getAnimal(favorite); // OK monkey.turn(); } }
結果、show1からshow3がコンパイルエラーになりました。show2は理解の範疇だったんですが、show3がコンパイルエラーになるのは以外でした。
でもまあ、よくよく考えると、クラスZooにパラメータを指定しなかった時点で、コンパイラは「zooはパラメータ非対応のZooの変数だな」と認識せざるを得ないわけですね。たとえ、パラメータEがgetAnimalメソッドのシクネイチャーに現れない場合であっても…です。
ためしに、クラスZooの定義からパラメータEを削除してみると、まあ当然ですがgetAnimalに直接関係のあるfavoriteのパラメータ有り無しだけが影響をおよぼすようになりました。
言語仕様的なエビデンスについてはまた今後。