いらっしゃいませ。今日は皆さんわんくま勉強会でしてねぇ。当BARも閑古鳥なんですよ。
でもまぁせっかくですし前回の
クイズの続きと行きましょう。Javaのジェネリクスの自信のほどはいかがですか?
前回は
List<String> listA = new ArrayList<String>();
List<? extends Object> listB = listA;
listB.add(new Object());
というコードでlistB = listAの代入はできるものの、listB.add()でコンパイルエラーとなるということでした。
では、次のコードをコンパイルおよび実行しようとするとどうなるでしょう?
List<Object> listA = new ArrayList<Object>();
List<? super String> listB = listA;
listB.add(new String());
正解は、何事もなく動くのです。
見慣れないかもしれませんが、ジェネリクスでは<? super ~>というように、
ある型をサブクラスとする何か、という表現ができます。extendsと逆ですね。
そして、今の場合はlistBはStringの親となる型であれば何でも格納できます。
前回との対比でString型としましたが、String型はfinalですので説明しやすい型に差し替えて説明いたしましょう。
以下に出てくるクラスは C extends B、 B extends Aとなっています。Aが一番上で、Bはそのサブクラス、Cはさらにそのサブクラスです。
List<A> listA = new ArrayList<A>();
List<B> listB = new ArrayList<B>();
List<C> listC = new ArrayList<C>();
List<? extends B> list1;
list1 = listA; // NG
list1 = listB; // OK
list1 = listC; // OK
list1.add(new A()); // NG
list1.add(new B()); // NG
list1.add(new C()); // NG
List<? super B> list2;
list2 = listA; // OK
list2 = listB; // OK
list2 = listC; // NG
list2.add(new A()); // NG
list2.add(new B()); // OK
list2.add(new C()); // OK
list1への代入はlistBとlistCだけOKです。
<? extends B>ですから、「Bを継承した何か」が格納されるListです。
listAはAが格納されています。AはBの親ですから、Bを継承していませんから当然NGです。
list1へのadd()がややこしいのですが、list1にはList<B>もList<C>も代入できます。
Cを継承したクラスがあればさらにそれらを格納するListも代入することが出来ますね。
そしてlist1.add()はこれらすべてに対して問題のない型しか格納できない理屈になります。
List<B>にもList<C>にもC型ならadd()できますが、継承は下へ下へと広がっていくものですから、
さらに継承階層が増える可能性がある以上、C型のadd()も認めるわけにはいきません。
かくして<? extends B>には何もadd()することができなくなるのです。
しかし、list2の方は趣が違っています。
<? super B>ですから、「Bの親の何か」なら格納できます。
ですから今度はlistAとlistBが代入できるようになります。ここまでは単にlist1のときの逆になっただけですね。
ここからが重要です。
list2に代入できるListはBで最後です。今回はA-B-Cの3段階の継承ですが、
100段階の継承だろうとsuperで明示された階層より下の型は代入できません。
ということは、Listの方の最下層であるBよりも継承階層が下の型はadd()しても大丈夫なんです。
listAにもlistBにもB型やC型は代入できます。listAよりも上の階層のListがきたって当然B型もC型も代入できます。
だから<? super ~>のときは指定した型より
下の階層のオブジェクトならadd()することができるのですね。
投稿日時 : 2007年8月4日 23:06