51

extendsJava Generics のキーワードを理解しようとしています。

List<? extends Animal>IS Aに任意のオブジェクトを詰め込むことができることを意味しListます Animal

次に、次も同じことを意味しません。

List<Animal>

誰かが上記の2つの違いを知るのを手伝ってもらえますか? extendsここでは冗長に聞こえます。

ありがとう!

4

3 に答える 3

77

List<Dog>は のサブタイプですが、 のサブタイプではありList<? extends Animal>ませんList<Animal>

List<Dog>がのサブタイプでないのはなぜList<Animal>ですか? 次の例を検討してください。

void mySub(List<Animal> myList) {
    myList.add(new Cat());
}

この関数にa を渡すことができるとList<Dog>、実行時エラーが発生します。


編集:List<? extends Animal>代わりに使用すると、次のようになります。

void mySub(List<? extends Animal> myList) {
    myList.add(new Cat());     // compile error here
    Animal a = myList.get(0);  // works fine 
}

この関数にa を渡すこともできList<Dog>ますが、コンパイラは、リストに何かを追加すると問題が発生する可能性があることを認識しています。(a を渡せるようにする)superの代わりに使用すると、逆になります。extendsList<LifeForm>

void mySub(List<? super Animal> myList) {
    myList.add(new Cat());     // works fine
    Animal a = myList.get(0);  // compile error here, since the list entry could be a Plant
}

この背後にある理論は、共変性と反変性です。

于 2010-04-04T18:21:28.837 に答える
47

を使用List<Animal>すると、持っているものが間違いなく動物のリストであることがわかります。それらのすべてが実際に正確に「動物」である必要はありません。それらは派生型であってもかまいません。たとえば、動物のリストがある場合、カップルがヤギであり、そのうちのいくつかが猫である可能性があることは理にかなっています。

たとえば、これは完全に有効です。

List<Animal> aL= new List<Animal>();
aL.add(new Goat());
aL.add(new Cat());
Animal a = aL.peek();
a.walk(); // assuming walk is a method within Animal

もちろん、以下は有効ではありません。

aL.peek().meow(); // we can't do this, as it's not guaranteed that aL.peek() will be a Cat

を使用すると、扱っているリストList<? extends Animal>のタイプについてのステートメントを作成できます。

例えば:

List<? extends Animal> L;

これは実際には、L が保持できるオブジェクトの型の宣言ではありませんこれは、L が参照できるリストの種類についてのステートメントです。

たとえば、次のようにします。

L = aL; // remember aL is a List of Animals

しかし今、コンパイラが L について知っているのは、それが[Animal または Animal のサブタイプのいずれか] のリストであることだけです。

したがって、以下は無効です。

L.add(new Animal()); // throws a compiletime error

私たちが知っている限りでは、L はヤギのリストを参照している可能性があります。これにはアニマルを追加できません。

理由は次のとおりです。

List<Goat> gL = new List<Goat>(); // fine
gL.add(new Goat()); // fine
gL.add(new Animal()); // compiletime error

上記では、Animal を Goat としてキャストしようとしています。それは機能しません。なぜなら、それを行った後、その動物にヤギのように「頭突き」をさせようとした場合はどうなるでしょうか? 動物がそれをできることを必ずしも知っているわけではありません。

于 2010-04-04T19:24:17.403 に答える
16

そうではない。List<Animal>この変数に割り当てられる値は "type" でなければならないことを示していますList<Animal>Animalただし、これはオブジェクトだけが存在しなければならないという意味ではなく、サブクラスも存在する可能性があります。

List<Number> l = new ArrayList<Number>();
l.add(4); // autoboxing to Integer
l.add(6.7); // autoboxing to Double

List<? extends Number>オブジェクトを取得したリストに関心がある場合は、構造体を使用しますNumberが、 List オブジェクト自体はタイプである必要はありませんがList<Number>、サブクラスの他のリスト ( などList<Integer>) にすることができます。

これは、メソッドの引数で「 のリストが必要ですNumbersが、それがただの であるかどうかは気にしません。List<Number>それも である可能性がありList<Double>ます」と言うために使用されることがあります。これにより、いくつかのサブクラスのリストがある場合、いくつかの奇妙なダウン キャストが回避されますが、メソッドはベースクラスのリストを想定しています。

public void doSomethingWith(List<Number> l) {
    ...
}

List<Double> d = new ArrayList<Double>();
doSomethingWith(d); // not working

これは期待どおりに機能していList<Number>ませんList<Double>。しかし、あなたが書いたなら、オブジェクトでなくてもオブジェクトList<? extends Number>を渡すことができます。List<Double>List<Number>

public void doSomethingWith(List<? extends Number> l) {
    ...
}

List<Double> d = new ArrayList<Double>();
doSomethingWith(d); // works

注:この全体は、リスト自体のオブジェクトの継承とは無関係です。要素の有無にかかわらず、リストにDoubleオブジェクトIntegerを追加できます。List<Number>? extends

于 2010-04-04T18:30:54.323 に答える