web-dev-qa-db-ja.com

ワイルドカードを含むリストは一般的なブードゥーエラーを引き起こします

次のコードがコンパイルされない理由を誰かが知っていますか? add()もaddAll()も期待どおりに機能しません。 「?extends」部分を削除するとすべてが機能しますが、Fooのサブクラスを追加できなくなります。

 List<? extends Foo> list1 = new ArrayList<Foo>();
 List<? extends Foo> list2 = new ArrayList<Foo>();

 /* Won't compile */
 list2.add( new Foo() ); //error 1
 list1.addAll(list2);    //error 2 

エラー1:

IntelliJさんのコメント:

add(capture<? extends Foo>) in List cannot be applied to add(Foo)

コンパイラは言う:

cannot find symbol
symbol  : method addAll(Java.util.List<capture#692 of ? extends Foo>)
location: interface Java.util.List<capture#128 of ? extends Foo>

エラー2:

IntelliJがくれます

addAll(Java.util.Collection<? extends capture<? extends Foo>>) in List cannot be applied to addAll(Java.util.List<capture<? extends Foo>>)

コンパイラはただ言うのに対し

cannot find symbol
symbol  : method addAll(Java.util.List<capture#692 of ? extends Foo>)
location: interface Java.util.List<capture#128 of ? extends Foo>
        list1.addAll(list2);
39
oligofren

(ここでは、BarBazはどちらもFooのサブタイプであると想定しています。)

_List<? extends Foo>_はFooのサブタイプであるいくつかのタイプの要素のリストですが、どのタイプかはわかりませんを意味します。そのようなリストの例は、_ArrayList<Foo>_、_LinkedList<Bar>_、および_ArrayList<Baz>_です。

型パラメーターがどのサブタイプであるかわからないため、FooオブジェクトをBarオブジェクトまたはBazオブジェクトに入れることはできません。ただし、typeパラメータがFooのサブタイプであることはわかっています。そのため、リストに既に存在するすべての要素(およびリストから取得できる要素)はFooオブジェクトである必要があります。 Foo f = list.get(0);などを使用します。

そのようなリストは、リストから要素を取り出すためにのみ使用でき、要素を追加するためには使用できません(nullは別ですが、コンパイラーが実際にこれを許可しているかどうかはわかりません)。

一方、_List<Foo>_は、Fooオブジェクトであるオブジェクトを追加できます。BarBazは、Fooのサブタイプであるため、 BarおよびBazオブジェクトはすべてFooオブジェクトなので、追加することもできます。

59
Paŭlo Ebermann

PECSを覚えておいてください: プロデューサー拡張、コンシューマースーパー

リスト2にアイテムを追加しようとしているため、これはコンシューマーであり、List<? extends Foo>として宣言できません。しかし、list1に追加するときも、list2をプロデューサーとして使用しています。したがって、list2はプロデューサーとコンシューマーの両方であり、List<Foo>である必要があります。

純粋なコンシューマとしてのlist1は、List<? super Foo>にすることができます。

26
Michael Myers

エラーです。 BarBazFooを拡張する2つの異なる型であることを考慮して、コードを変更してみましょう。

_List<? extends Foo> list1 = new ArrayList<Bar>();
List<? extends Foo> list2 = new ArrayList<Baz>();
_

list1.add(new Foo())が許可されている場合、Barインスタンスを含むコレクションにFooインスタンスを追加できます。これは最初のエラーを説明します。

list1.addAll(list2)が許可されている場合、list2のBazのすべてのインスタンスは、Barインスタンスのみを含むlist1に追加されます。これは2番目のエラーを説明します。

8
Vivien Barousse

<? extend Classname>を使用する必要がある場合について説明します。

したがって、2つのクラスがあるとします。

class Grand {
    private String name;

    public Grand(String name) {
        this.setName(name);
    }

    public Grand() {
    }

    public void setName(String name) {
        this.name = name;
    }
}

class Dad extends Grand {
    public Dad(String name) {
        this.setName(name);
    }

    public Dad() {
    }
}

また、2つのコレクションがあり、それぞれにいくつかのグランドとダッドが含まれているとします。

    List<Dad> dads = new ArrayList<>();
    dads.add(new Dad("Dad 1"));
    dads.add(new Dad("Dad 2"));
    dads.add(new Dad("Dad 3"));


    List<Dad> grands = new ArrayList<>();
    dads.add(new Dad("Grandpa 1"));
    dads.add(new Dad("Grandpa 2"));
    dads.add(new Dad("Grandpa 3"));

次に、GrandオブジェクトまたはDadオブジェクトを含むコレクションが必要だと仮定します。

        List<Grand> resultList;
        resultList = dads; // Error - Incompatable types List<Grand> List<Dad>
        resultList = grands;//Works fine

どうすればこれを回避できますか?単にワイルドカードを使用します:

List<? extends Grand> resultList;
resultList = dads; // Works fine
resultList = grands;//Works fine

そのような(resultList)コレクションに新しいアイテムを追加できないことに注意してください。詳細については、JavaでのワイルドカードとPECSの概念について読むことができます。

すみません、多分私はあなたの質問を誤解しましたが、疑いました:

public class Bar extends Foo{ }

このコード:

List<Foo> list2 = new ArrayList<Foo>()
list2.add( new Bar() );

エラーは発生しません。

したがって、ワイルドカードを削除すると、Fooのサブクラスを追加できます。

2
Tomas Narros