次のコードがコンパイルされない理由を誰かが知っていますか? 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);
(ここでは、Bar
とBaz
はどちらも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
オブジェクトであるオブジェクトを追加できます。Bar
とBaz
は、Foo
のサブタイプであるため、 Bar
およびBaz
オブジェクトはすべてFoo
オブジェクトなので、追加することもできます。
PECSを覚えておいてください: プロデューサー拡張、コンシューマースーパー 。
リスト2にアイテムを追加しようとしているため、これはコンシューマーであり、List<? extends Foo>
として宣言できません。しかし、list1に追加するときも、list2をプロデューサーとして使用しています。したがって、list2はプロデューサーとコンシューマーの両方であり、List<Foo>
である必要があります。
純粋なコンシューマとしてのlist1は、List<? super Foo>
にすることができます。
エラーです。 Bar
とBaz
がFoo
を拡張する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番目のエラーを説明します。
<? 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のサブクラスを追加できます。