前回はまず、ジェネリクス型パラメータを伴うList同士の代入互換性について述べました。
今回はそれらのListのadd()メソッドとget()メソッドについて見てきたいと思います。
なお、前回同様に C extends B,
B extends A
という継承関係があることとして以下話を進めます。
入力値の制約
前回で<? extends B>型には
<B>も
<C>も
<? extends C>も代入できると述べました。
List<? extends B> listBEx = new ArrayList<C>();
ということができるわけですね。
さて、このlistBExにadd()をしてみるとしましょう。
listBEx.add(new B());
実は、これがコンパイルエラーになるのです。
List<? extends B>型には
B型をadd()できないのです!
というのも、さきほどlistBExはArrayList<C>型で
初期化しましたね。もし、Bをadd()できるとしたら、
ArrayList<C>型に
B型がadd()されてしまうことになります。これでは矛盾してしまいますね。
ですから、List<? extends B>では
型の安全性が破壊されないように、add()できるのは
List<? extends B>に代入可能な
List全てにadd()可能なものだけしかadd()できないように制約が掛けられます。
<? extends B>の範囲と
ArrayList<C>の範囲、
そしてBオブジェクトの位置を確認してみてください。
Bオブジェクトを型安全にadd()することができないのが分かるでしょうか?
では<? extends B>に対して何がadd()できるのでしょうか?
listBEx.add(null);
だけが可能なのです。使えませんね…。
さて、add()メソッドはこのような制約があるわけですが、get()メソッドなどは普通に使えます。
この違いは何なのでしょうか?
これは、メソッドの引数にジェネリクス型パラメータが含まれる場合に発生する制約です。
引数にジェネリクス型が含まれるadd()などではこのような制約が発生し、
引数にジェネリクス型を含まないget()などでは制約は発生しません。
オブジェクトに対しての入力値がジェネリクスの代入互換性に矛盾しないようにするために
存在する制約というわけなのです。
出力値の制約
List<? extends B>からの
get()は問題なく行え、B型の変数に受け取ることができます。
B b = listBEx.get(0);
これは、<? extends B>に
<B>が代入されていようが
<C>が代入されていようが、
get()で取り出されるオブジェクトは「B型を継承した何か」ですから、B型に安全にキャストできるわけです。
ここで、List<? super B>を考えてみましょう。
<? super B>には
<B>や
<A>を代入することができます。
ということは、get()で取り出されるオブジェクトは、B型よりも上位のオブジェクト型である可能性があるわけです。
このことから、<? super B>とした場合は
全ての型のトップに位置するObject型でしかget()したオブジェクトを受け取ることができません。
Object o = listBSu.get(0);
これは前回の図を見ると分かることでしょう。再掲します。
入力値の制約 再訪
List<? super B>へのadd()はどうでしょうか?
図を見て分かるように、<? super B>に
代入可能な<B>や
<A>は、すべてBオブジェクトを
受け入れることができます。
そのため、ジェネリクスが<? super B>であれば
B型をadd()することができます。
投稿日時 : 2008年8月20日 0:08