凪瀬 Blog
Programming SHOT BAR

目次

Blog 利用状況
  • 投稿数 - 260
  • 記事 - 0
  • コメント - 2527
  • トラックバック - 192
ニュース
広告
  • Java開発者募集中
  • 経歴不問
  • 腕に自信のある方
  • 富山市内
  • (株)凪瀬アーキテクツ
アクセサリ
  • あわせて読みたい
凪瀬悠輝(なぎせ ゆうき)
  • Java技術者
  • お茶好き。カクテル好き。
  • 所属は(株)凪瀬アーキテクツ
  • Twitter:@nagise

書庫

日記カテゴリ

 

前回はまず、ジェネリクス型パラメータを伴う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への代入

<? 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);

これは前回の図を見ると分かることでしょう。再掲します。

階層? super B

入力値の制約 再訪

List<? super B>へのadd()はどうでしょうか?

? super Bへの代入

図を見て分かるように、<? super B>に 代入可能な<B>や <A>は、すべてBオブジェクトを 受け入れることができます。

そのため、ジェネリクスが<? super B>であれば B型をadd()することができます。

投稿日時 : 2008年8月20日 0:08
コメント
  • # re: ジェネリクスの代入を理解する その2
    Moo
    Posted @ 2008/10/29 16:37
    <? extends B>にnullしかaddできない理由がよくわかりました。ありがとうございます。

    ところで、

     List<? super B> list = new ArrayList<A>();

     list.add(new B());  //1
     list.add(new A()); //2
     list.add(new C()); //3

    この書き方だと、2だけがコンパイルエラーになるのですが、これはどう考えたらよいのでしょうか?
  • # re: ジェネリクスの代入を理解する その2
    凪瀬
    Posted @ 2008/10/29 20:17
    List<? super B> list = new ArrayList<B>();
    のとき、
    List<? super B>型にAをadd()するとArrayList<B>にA型がaddされることになって矛盾しますよね。

    ですから、List<? super B>型に代入可能な、一番具体的な型であるBに代入可能な型しかadd()できないんです。
    つまり、Bもしくはそのサブクラスのみadd()可能となります。
  • # re: ジェネリクスの代入を理解する その2
    Moo
    Posted @ 2008/10/30 11:41
    なるほど。よくわかりました。
    ありがとうございました。
  • # re: ジェネリクスの代入を理解する その2
    SJC-P受験予定者
    Posted @ 2009/01/03 23:29
    はじめまして。横からすみません。

    >List<? super B> list = new ArrayList<B>(); のとき、
    >List<? super B>型にAをadd()するとArrayList<B>にA型がaddされることになって矛盾しますよね

    この意味は分かりました。

    しかし、インタフェースIFをAが実装しているとし、
    A←B←Cの関係はそのままの場合で下記処理をコンパイルしたとします。
    List<? super B> list = new ArrayList<IF>
    list.add(new B()); //1
    list.add(new A()); //2
    list.add(new C()); //3

    この場合も2はエラーとなってしまいます。

    ArrayList<IF>にAをaddしても矛盾しないと思うのですが、
    これは何故なのでしょうか??
  • # ジェネリクスの代入互換の補足
    凪瀬 Blog
    Posted @ 2009/01/04 18:07
    ジェネリクスの代入互換の補足
  • # DRBRLlCpAvCO
    http://www.hooksandlattice.com
    Posted @ 2011/12/27 18:46
    Yeah !... life is like riding a bicycle. You will not fall unless you stop pedaling!!...
  • # AYkfsYWySatEkTDEK
    http://www.luckyvitamin.com/m-126-biotics-research
    Posted @ 2012/01/07 8:28
    Hi! Everyone who reads this blog - Happy Reconciliation and Accord..!
  • # re: ジェネリクスの代入を理解する その2
    かず
    Posted @ 2015/11/16 22:58
    初めまして。Javaの勉強中に辿り着いた者ですが質問させてください。

    <? extends B>には、add(null)しか出来ない。
    B b = listBEx.get(0);は可能。

    という事ですが、null以外をadd() 出来るケースはどういうケースなのでしょうか?
タイトル  
名前  
Url
コメント