個人の日記レベルですんません、現象のみ。
書いてるコードに関しては型指定してあげれば解決したのだけど、再現させるのに苦労した。
まず、インタフェースを2つ用意。
interface A{} interface B{}
それから、その両方を実装したクラスをふたつ用意
static class C implements A,B{} static class D implements A,B{}
片方だけ実装したクラスもふたつ用意
static class E implements A{} static class F implements A{}
そんで、同じ型を継承してGでくるんだものを複数とって、共通の基底クラスをGでくるんだものとして返すメソッドを定義。
※この文章では、実装したインタフェースも基底クラスと表現します。
static <T> G<T> o(G<? extends T>... t){ return null; }
このメソッドoに、CをくるんだGと、DをくるんだGを渡す。CとDの共通の基底クラスはAとBなので、G<A>とG<B>として受け取ることができる。これはOK
G<A> o01 = o(new G<C>(), new G<D>());//OK G<B> o01 = o(new G<C>(), new G<D>());//OK
それから、Genericなクラスを用意して、同じ型を返すメソッドと同じ型のリストをくるむメソッドを定義。今回は型だけの問題なので、戻り値はnull。
static class G<U>{ G<U> m(){ return null; } G<List<U>> n(){ return null; } }
ここで、メソッドoの戻り値にメソッドmの呼び出しを追加するとコンパイルエラー
G<A> o02 = o(new G<C>(), new G<D>()).m();//NG G<B> o03 = o(new G<C>(), new G<D>()).m();//NG
戻り値を使わなければOK。このあとに出る他のNGのやつも、戻り値使わなければだいたいOKです。
o(new G<C>(), new G<D>()).m();//NG
変数の型をAを継承したものと指定するか、メソッドoの呼び出しでgeneric型を指定してあげればOK。generic型を指定すると大丈夫ということから、型推論が失敗してることがわかる。
G<? extends A> o04 = o(new G<C>(), new G<D>()).m();//OK G<A> o05 = SqlParser.<A>o(new G<C>(), new G<D>()).m();//OK
さっき失敗したものを、もう一度メソッドoに渡してやるとOK
G<A> o06 = o(o(new G<C>(), new G<D>()).m());//OK
共通の基底クラスがAだけのEとFならOK
G<A> o07 = o(new G<E>(), new G<F>()).m();//OK G<A> o08 = o(o(new G<E>(), new G<F>()).m());//OK
メソッドoの引数にCとDに加えてEを付け足してもOK
G<A> o09 = o(new G<C>(), new G<D>(), new G<E>()).m();//OK
メソッドoのあとに付け加えるのをメソッドnにして、Listを返すようにするとやはりNG。変数の型の範囲をひろげてもだめ。
G<List<A>> o11 = o(new G<C>(), new G<D>()).n();//NG G<List<? extends A>> o12 = o(new G<C>(), new G<D>()).n();//NG
メソッドoにちゃんと型を指定してあげると大丈夫
G<List<A>> o13 = SqlParser.<A>o(new G<C>(), new G<D>()).n();//OK
もう一回メソッドoに渡す作戦も、今回はNG
G<List<A>> o14 = o(o(new G<C>(), new G<D>()).n());//NG
内側のメソッドoにちゃんと型を指定してあげると大丈夫
G<List<A>> o15 = o(SqlParser.<A>o(new G<C>(), new G<D>()).n());//OK
外側のメソッドoに型を指定しても内側でNG。これは戻り値使わなくてもNG
G<List<A>> o16 = SqlParser.<A>o(o(new G<C>(), new G<D>()).n());//NG
共通の基底クラスがAだけのEとFなら大丈夫。
G<List<A>> o17 = o(new G<E>(), new G<F>()).n();//OK G<List<A>> o18 = o(o(new G<E>(), new G<F>()).n());//OK
ということで、これひっかかるとすげーハマります。
こういうのはどこまで仕様なんだろう?