Javaのジェネリクスはかなり強力で、相当の型を表現できるのですが、
代償として非常に複雑なものとなっています。
ややこしいのは、オブジェクト指向の部分の型の代入互換性と、
ジェネリクス型パラメータの部分の代入互換性は、表現こそ似ているものの、
その意味するところはまるで違うと言うことにあります。
端的には、C extends B,
B extends Aの関係があるとして、
型B にはサブクラスであるCをキャストなしに安全に代入することができます。
B b = new C();
しかし、ジェネリクス型パラメータの場合の
List<B> listB = new ArrayList<C>();
はコンパイルエラーとなります。
List<? extends B> listBEx = new ArrayList<C>();
であれば代入が可能です。
このように、同じ継承階層の型を扱うのにもかかわらず、その代入互換性が違うのですから
混乱するのはやむなしと言えましょう。
ジェネリクス型パラメータの代入互換性
ジェネリクスでは、単に型をBと表現した場合、Bの階層だけが対象となります。
図のBの階層だけが対象になります。
ですから、代入できるのは<B>型だけです。
List<B> listB = new ArrayList<B>();
次に、<? extends B>と表現した場合、BとそのサブクラスであるCが含まれます。
<? extends B>には<B>も<C>も代入することができます。
また、<? extends C>も代入することができます。
List<? extends B> listBEx;
listBEx = new ArrayList<B>();
listBEx = new ArrayList<C>();
List<? extends C> listCEx = new ArrayList<C>();
listBEx = listCEx;
また、ジェネリクスでは継承階層をスーパークラス側に遡る、<? super B>という記述もできます。
この場合は、<? extends B>とは逆方向の範囲をカバーします。
この<? super B>には<B>や<A>や<? super A>を代入することができます。
List<? super B> listBSu;
listBSu = new ArrayList<B>();
listBSu = new ArrayList<A>();
List<? super A> listASu = new ArrayList<A>();
listBSu = listASu;
このように、<>の内側と外側では異なる代入規則があることをまずは明確に意識してください。
投稿日時 : 2008年8月18日 23:35