違いは何である<? super E>
とは<? extends E>
?
たとえば、クラスを見るjava.util.concurrent.LinkedBlockingQueue
と、コンストラクタに次のシグネチャがあります。
public LinkedBlockingQueue(Collection<? extends E> c)
そしてメソッドのために:
public int drainTo(Collection<? super E> c)
違いは何である<? super E>
とは<? extends E>
?
たとえば、クラスを見るjava.util.concurrent.LinkedBlockingQueue
と、コンストラクタに次のシグネチャがあります。
public LinkedBlockingQueue(Collection<? extends E> c)
そしてメソッドのために:
public int drainTo(Collection<? super E> c)
回答:
最初はそれが「Eの祖先であるあるタイプ」であると言います。2番目は、「Eのサブクラスであるタイプ」であると述べています。(どちらの場合も、E自体は問題ありません。)
そのため、コンストラクタは? extends E
フォームを使用して、コレクションから値をフェッチするときに、すべてがEまたはサブクラスになることを保証します(つまり、互換性があります)。drainTo
この方法は、値を入れしようとしているにコレクションはの要素の型持たなければならないので、コレクションE
やスーパークラスを。
例として、次のようなクラス階層があるとします。
Parent extends Object
Child extends Parent
とLinkedBlockingQueue<Parent>
。List<Child>
すべてChild
が親であるため、すべての要素を安全にコピーする、この受け渡しを構築できます。List<Object>
一部の要素はと互換性がない可能性があるため、aを渡すことができませんでしたParent
。
同様に、List<Object>
すべてParent
がObject
...であるため、そのキューをに排出できますがList<Child>
、List<Child>
はそのすべての要素がと互換性があると想定しているため、キューをに排出できませんでしたChild
。
? extends InputStream
たり? super InputStream
、あなたが使用できるInputStream
引数として。
この理由は、Javaがジェネリックを実装する方法に基づいています。
配列の例
配列でこれを行うことができます(配列は共変です)
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
しかし、これを行おうとするとどうなりますか?
myNumber[0] = 3.14; //attempt of heap pollution
この最後の行は問題なくコンパイルされますが、このコードを実行すると、を取得できますArrayStoreException
。(数値参照を通じてアクセスされているかどうかに関係なく)整数配列にdoubleを入れようとしているためです。
これは、コンパイラをだますことができても、ランタイム型システムをだますことができないことを意味します。これは、配列がreifiable型と呼ばれるものだからです。つまり、実行時にJavaは、この配列が実際には整数の配列としてインスタンス化され、たまたまtypeの参照を通じてアクセスされることを知っていますNumber[]
。
ご覧のとおり、1つはオブジェクトの実際のタイプで、もう1つはオブジェクトへのアクセスに使用する参照のタイプです。
Java Genericsの問題
現在、Javaジェネリック型の問題は、型情報がコンパイラによって破棄され、実行時に利用できないことです。このプロセスはタイプ消去と呼ばれます。このようなジェネリックをJavaで実装することには十分な理由がありますが、それは長い話であり、とりわけ、既存のコードとのバイナリー互換性と関係があります(ジェネリックの取得方法を参照)。
ただし、ここで重要な点は、実行時にはタイプ情報がないため、ヒープ汚染をコミットしていないことを確認する方法がないということです。
例えば、
List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap pollution
Javaコンパイラーがこれを止めない場合、ランタイム型システムもあなたを止めることはできません。これは、実行時に、このリストが整数のリストのみであることになっていると判断する方法がないためです。作成時に整数のリストとして宣言されていたため、Javaランタイムは整数のみを含む必要がある場合、このリストに何でも入れることができます。
そのため、Javaの設計者は、コンパイラーをだまさないようにしています。コンパイラーを欺くことができない場合(配列の場合と同様)、ランタイム型システムをだますこともできません。
そのため、ジェネリック型は変更不可能であると言います。
明らかに、これは多態性を妨げます。次の例を検討してください。
static long sum(Number[] numbers) {
long summation = 0;
for(Number number : numbers) {
summation += number.longValue();
}
return summation;
}
これで、次のように使用できます。
Integer[] myInts = {1,2,3,4,5};
Long[] myLongs = {1L, 2L, 3L, 4L, 5L};
Double[] myDoubles = {1.0, 2.0, 3.0, 4.0, 5.0};
System.out.println(sum(myInts));
System.out.println(sum(myLongs));
System.out.println(sum(myDoubles));
ただし、ジェネリックコレクションを使用して同じコードを実装しようとすると、成功しません。
static long sum(List<Number> numbers) {
long summation = 0;
for(Number number : numbers) {
summation += number.longValue();
}
return summation;
}
あなたがしようとすると、コンパイラエラーが発生します...
List<Integer> myInts = asList(1,2,3,4,5);
List<Long> myLongs = asList(1L, 2L, 3L, 4L, 5L);
List<Double> myDoubles = asList(1.0, 2.0, 3.0, 4.0, 5.0);
System.out.println(sum(myInts)); //compiler error
System.out.println(sum(myLongs)); //compiler error
System.out.println(sum(myDoubles)); //compiler error
解決策は、共分散と反変と呼ばれるJavaジェネリックの2つの強力な機能の使い方を学ぶことです。
共分散
共分散を使用すると、構造から項目を読み取ることはできますが、構造に何かを書き込むことはできません。これらはすべて有効な宣言です。
List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>();
List<? extends Number> myNums = new ArrayList<Double>();
そして、あなたはから読むことができますmyNums
:
Number n = myNums.get(0);
実際のリストに何が含まれていても、それをNumberにアップキャストすることができます(Numberを拡張するものはすべてNumberになるのでしょ?)
ただし、共変構造に何かを入れることはできません。
myNumst.add(45L); //compiler error
Javaはジェネリック構造内のオブジェクトの実際のタイプを保証できないため、これは許可されません。Numberを拡張するものであれば何でもかまいませんが、コンパイラーは確実ではありません。したがって、読み取りはできますが、書き込みはできません。
反変
逆分散を使用すると、反対のことができます。一般的な構造に物を入れることはできますが、そこから読み取ることはできません。
List<Object> myObjs = new List<Object>();
myObjs.add("Luke");
myObjs.add("Obi-wan");
List<? super Number> myNums = myObjs;
myNums.add(10);
myNums.add(3.14);
この場合、オブジェクトの実際の性質はオブジェクトのリストであり、基本的にすべての数値が共通の祖先としてObjectを持っているため、反変によってNumbersをそこに入れることができます。そのため、すべての数値はオブジェクトであるため、これは有効です。
ただし、数値を取得することを前提として、この反変構造から何も安全に読み取ることはできません。
Number myNum = myNums.get(0); //compiler-error
ご覧のように、コンパイラがこの行の記述を許可した場合、実行時にClassCastExceptionが発生します。
Get / Put Principle
そのため、構造体から総称値のみを取得する場合は共分散を使用し、総称値を構造体に配置するだけの場合は反変を使用し、両方を実行する場合は正確な総称型を使用します。
私が持っている最も良い例は、あるリストから別のリストにあらゆる種類の数値をコピーする次のとおりです。それが唯一の取得源からのアイテムを、そしてそれだけ置くターゲットにアイテムを。
public static void copy(List<? extends Number> source, List<? super Number> target) {
for(Number number : source) {
target(number);
}
}
共分散と反変の力のおかげで、これは次のような場合に機能します。
List<Integer> myInts = asList(1,2,3,4);
List<Double> myDoubles = asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();
copy(myInts, myObjs);
copy(myDoubles, myObjs);
List<Object> myObjs = new List<Object();
(これは>
2番目の終わりを欠いているObject
)。
super.methodName
。を使用する場合<? super E>
、super
方向の何かではなく、「方向の何か」を意味しextends
ます。例は:Object
であるsuper
の方向Number
(それはスーパークラスであるため)とInteger
しているextends
(それが延びているので方向Number
)。
<? extends E>
E
上限として定義します:「これはにキャストできますE
」。
<? super E>
E
下限として定義します:「E
これにキャストできます。」
Object
は本質的に下位レベルのクラスですが、それは究極のスーパークラス(およびUMLまたは同様の継承ツリーで垂直方向に描画)としての位置にもかかわらずです。何年も試しても、これを元に戻すことはできませんでした。
これに答えてみます。しかし、本当に良い答えを得るには、Joshua Blochの著書「Effective Java(2nd Edition)」をチェックする必要があります。彼は「Producer Extends、Consumer Super」の略であるニーモニックPECSについて説明しています。
アイデアは、コードがオブジェクトからのジェネリック値を消費する場合は、extendsを使用する必要があるということです。ただし、ジェネリック型の新しい値を生成する場合は、superを使用する必要があります。
だから例えば:
public void pushAll(Iterable<? extends E> src) {
for (E e: src)
push(e);
}
そして
public void popAll(Collection<? super E> dst) {
while (!isEmpty())
dst.add(pop())
}
しかし、実際にはこの本をチェックする必要があります:http : //java.sun.com/docs/books/effective/
<? super E>
手段 any object including E that is parent of E
<? extends E>
手段 any object including E that is child of E .
反変()と共分散()という用語についてグーグル することをお勧めします。ジェネリックスを理解するときに最も役立つのは、次のメソッドのシグネチャを理解することでした。<? super E>
<? extends E>
Collection.addAll
public interface Collection<T> {
public boolean addAll(Collection<? extends T> c);
}
String
にa を追加できるようにしたいのと同じようにList<Object>
:
List<Object> lo = ...
lo.add("Hello")
メソッドを介してList<String>
(またはの任意のコレクションString
)を追加することもできますaddAll
。
List<String> ls = ...
lo.addAll(ls)
ただし、a List<Object>
とa List<String>
は同等ではなく、後者は前者のサブクラスではないことを理解する必要があります。必要なのは、共変型パラメーターの概念です。つまり、<? extends T>
ビットです。
これができたら、反変も必要なシナリオを考えるのは簡単です(Comparable
インターフェースを確認してください)。
答えの前に; 明確にしてください
例:
List stringList = new ArrayList<String>();
stringList.add(new Integer(10)); // will be successful.
これがワイルドカードをより明確に理解するのに役立つことを願っています。
//NOTE CE - Compilation Error
// 4 - For
class A {}
class B extends A {}
public class Test {
public static void main(String args[]) {
A aObj = new A();
B bObj = new B();
//We can add object of same type (A) or its subType is legal
List<A> list_A = new ArrayList<A>();
list_A.add(aObj);
list_A.add(bObj); // A aObj = new B(); //Valid
//list_A.add(new String()); Compilation error (CE);
//can't add other type A aObj != new String();
//We can add object of same type (B) or its subType is legal
List<B> list_B = new ArrayList<B>();
//list_B.add(aObj); CE; can't add super type obj to subclass reference
//Above is wrong similar like B bObj = new A(); which is wrong
list_B.add(bObj);
//Wild card (?) must only come for the reference (left side)
//Both the below are wrong;
//List<? super A> wildCard_Wrongly_Used = new ArrayList<? super A>();
//List<? extends A> wildCard_Wrongly_Used = new ArrayList<? extends A>();
//Both <? extends A>; and <? super A> reference will accept = new ArrayList<A>
List<? super A> list_4__A_AND_SuperClass_A = new ArrayList<A>();
list_4__A_AND_SuperClass_A = new ArrayList<Object>();
//list_4_A_AND_SuperClass_A = new ArrayList<B>(); CE B is SubClass of A
//list_4_A_AND_SuperClass_A = new ArrayList<String>(); CE String is not super of A
List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<A>();
list_4__A_AND_SubClass_A = new ArrayList<B>();
//list_4__A_AND_SubClass_A = new ArrayList<Object>(); CE Object is SuperClass of A
//CE; super reference, only accepts list of A or its super classes.
//List<? super A> list_4__A_AND_SuperClass_A = new ArrayList<String>();
//CE; extends reference, only accepts list of A or its sub classes.
//List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<Object>();
//With super keyword we can use the same reference to add objects
//Any sub class object can be assigned to super class reference (A)
list_4__A_AND_SuperClass_A.add(aObj);
list_4__A_AND_SuperClass_A.add(bObj); // A aObj = new B();
//list_4__A_AND_SuperClass_A.add(new Object()); // A aObj != new Object();
//list_4__A_AND_SuperClass_A.add(new String()); CE can't add other type
//We can't put anything into "? extends" structure.
//list_4__A_AND_SubClass_A.add(aObj); compilation error
//list_4__A_AND_SubClass_A.add(bObj); compilation error
//list_4__A_AND_SubClass_A.add(""); compilation error
//The Reason is below
//List<Apple> apples = new ArrayList<Apple>();
//List<? extends Fruit> fruits = apples;
//fruits.add(new Strawberry()); THIS IS WORNG :)
//Use the ? extends wildcard if you need to retrieve object from a data structure.
//Use the ? super wildcard if you need to put objects in a data structure.
//If you need to do both things, don't use any wildcard.
//Another Solution
//We need a strong reference(without wild card) to add objects
list_A = (ArrayList<A>) list_4__A_AND_SubClass_A;
list_A.add(aObj);
list_A.add(bObj);
list_B = (List<B>) list_4__A_AND_SubClass_A;
//list_B.add(aObj); compilation error
list_B.add(bObj);
private Map<Class<? extends Animal>, List<? extends Animal>> animalListMap;
public void registerAnimal(Class<? extends Animal> animalClass, Animal animalObject) {
if (animalListMap.containsKey(animalClass)) {
//Append to the existing List
/* The ? extends Animal is a wildcard bounded by the Animal class. So animalListMap.get(animalObject);
could return a List<Donkey>, List<Mouse>, List<Pikachu>, assuming Donkey, Mouse, and Pikachu were all sub classes of Animal.
However, with the wildcard, you are telling the compiler that you don't care what the actual type is as long as it is a sub type of Animal.
*/
//List<? extends Animal> animalList = animalListMap.get(animalObject);
//animalList.add(animalObject); //Compilation Error because of List<? extends Animal>
List<Animal> animalList = animalListMap.get(animalObject);
animalList.add(animalObject);
}
}
}
}