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のパラメータ有り無しだけが影響をおよぼすようになりました。

言語仕様的なエビデンスについてはまた今後。