extends
Java Generics のキーワードを理解しようとしています。
List<? extends Animal>
IS Aに任意のオブジェクトを詰め込むことができることを意味しList
ます Animal
次に、次も同じことを意味しません。
List<Animal>
誰かが上記の2つの違いを知るのを手伝ってもらえますか? extends
ここでは冗長に聞こえます。
ありがとう!
extends
Java Generics のキーワードを理解しようとしています。
List<? extends Animal>
IS Aに任意のオブジェクトを詰め込むことができることを意味しList
ます Animal
次に、次も同じことを意味しません。
List<Animal>
誰かが上記の2つの違いを知るのを手伝ってもらえますか? extends
ここでは冗長に聞こえます。
ありがとう!
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
の代わりに使用すると、逆になります。extends
List<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
}
この背後にある理論は、共変性と反変性です。
を使用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 としてキャストしようとしています。それは機能しません。なぜなら、それを行った後、その動物にヤギのように「頭突き」をさせようとした場合はどうなるでしょうか? 動物がそれをできることを必ずしも知っているわけではありません。
そうではない。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