この理由は、Javaがジェネリックを実装する方法に基づいています。
アン配列の例は配列で
あなたはこれを行うにしようと何が起こるか、この(配列は共変です)
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
を行うことができますが?
Number[0] = 3.14; //attempt of heap pollution
この最後の行はうまくコンパイルだろうが、あなたはこのコードを実行した場合、あなたはArrayStoreException
を得ることができます。なぜなら、あなたは数値配列を通してアクセスされているかどうかに関わらず、整数配列にdoubleを入れることを試みているからです。
これは、コンパイラをだますことができますが、ランタイムタイプのシステムをだますことはできないことを意味します。配列はと呼ばれるものなので、これはそうです。です。これは実行時にJavaがこの配列が実際にはNumber[]
の参照でアクセスされる整数の配列として実際にインスタンス化されたことを知っていることを意味します。
ご覧のとおり、オブジェクトの実際のタイプと、アクセスに使用する参照のタイプの違いがあります。
Javaのジェネリック
を通報しますさて、Javaのジェネリック型の問題は、型情報がコンパイラによって破棄され、それが実行時に利用できないことです。このプロセスはtype erasureと呼ばれます。 Javaのようにジェネリックスを実装するのには良い理由がありますが、これは長い話です。既存のコードとのバイナリ互換性に関係しています。
ここで重要な点は、実行時に型情報がないため、ヒープ汚染をコミットしていないことを保証する方法がないことです。例えば
、
List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap polution
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
から読み取ることができます:あなたは、実際のリストが含まれているものは何でもことを確認することができますので数を拡張し、すべてのものは、番号は、後に、それは(数にupcastedすることができ
Number n = myNums.get(0);
、右?)
ただし、共変な構造に何かを入れることはできません。
myNumst.add(45L); //compiler error
Javaは、一般的な構造のオブジェクトの実際の型が何であるかを保証できないので、これは、許可されません。これは、Numberを拡張するものであれば何でも構いませんが、コンパイラは確実ではありません。あなたは読むことができますが、書くことはできません。 contravarianceで
Contravariance
あなたは反対のことを行うことができます。物事を一般的な構造にすることはできますが、そこから読み出すことはできません。この場合
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);
は、オブジェクトの実際の性質は、オブジェクトのリストであり、すべての数字は彼らの共通の祖先としてオブジェクトを持っているのでcontravarianceを通じて、あなたは基本的には、それに数字を置くことができます。したがって、すべての数値はオブジェクトなので、これは有効です。
しかし、あなたが数値を取得すると仮定して、この反変種構造から何かを安全に読み取ることはできません。
Number myNum = myNums.get(0); //compiler-error
ご覧のとおり、コンパイラがこの行を書くことを許可した場合、実行時にClassCastExceptionが発生します。あなただけの、contravarianceを使用した構造のうち、一般的な値を取るようにしようとするとき、あなただけの構造に一般的な値を入れて、正確なジェネリックを使用する際にこのよう
取得/入れ原理
、共分散を使用両方を行う場合は入力してください。
私が持っている最良の例は、ある種の数字をあるリストから別のリストにコピーする次のものです。 はのアイテムをソースから取得し、はのアイテムを運命に入れます。
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);
チェックこれをhttp://stackoverflow.com/questions/2745265/is-listdog-a-subclass:これは、このような場合のために働く共変性と反変性の力に
感謝-of-listanimal-why-arent-javas-generics-implicit – NPKR