なぜリスト<番号>ではないが一覧<オブジェクト>のサブタイプですか?
-
11-09-2019 - |
質問
public void wahey(List<Object> list) {}
wahey(new LinkedList<Number>());
メソッドの呼び出しは、入力チェックをしません。次のように私も、パラメータをキャストすることはできません。
wahey((List<Object>) new LinkedList<Number>());
は、私の研究から、私はこれを許可しない理由は、型の安全性であることを集めています。我々は上記を行うことを許されたならば、私たちは次のことを持っている可能性があります:
List<Double> ld;
wahey(ld);
(パラメータがList<Object>
の参照を保持して)メソッドwaheyの内部では、我々は、入力リストにいくつかの文字列を追加することができます。さて、メソッド呼び出しの後、ldは型List<Double>
でリストを参照し、実際のリストは、いくつかのStringオブジェクトが含まれています!
これは、Javaがジェネリックなしで動作し、通常の方法とは異なるようです。たとえばます:
Object o;
Double d;
String s;
o = s;
d = (Double) o;
これはコンパイル時のチェックをパスし、実行時にのみ失敗する以外私たちがここでやっていることは、本質的には同じものです。リストを使用したバージョンはコンパイルされません。
これは、これは純粋にジェネリックの型の制限に関してと設計上の決定であると信じて私をリードしています。私はこの決定にいくつかのコメントを取得するために期待していた?
解決
あなたが「ジェネリックなし」の例ではやっているあなたは、型安全ではない何かをしていることが明確にあることになるキャストは、あります。ジェネリック医薬品と同等では以下のようになります:
Object o;
List<Double> d;
String s;
o = s;
d.add((Double) o);
は同じように動作します(コンパイルを、しかし、実行時に失敗しました)。それは、コードに気づくことが非常に困難です。の暗黙のの型安全ではないアクション、できるようになるので、あなたは約求めている動作を許可しない理由はあります。たとえばます:
public void Foo(List<Object> list, Object obj) {
list.add(obj);
}
あなたはこのようにそれを呼び出すまで、これは完全に罰金とタイプセーフになります。
List<Double> list_d;
String s;
Foo(list_d, s);
、発信者としてあなたは、必ずしもfooはそのパラメータをどうする何が起こっているかわからないのでまた、タイプセーフに見えどのます。
だから、その場合には、あなたが一緒に型安全ではないになってしまうのコードの2一見タイプセーフビットを持っています。それは隠されたため、避けるのは難しいとデバッグが難しくなりますので、それは、悪いのです。
他のヒント
それがあった場合は考えてみましょう...
List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException
あなたは上記の例が示すように、あらゆる種類の問題を引き起こす、としてそれは、以前に宣言されたものではないかもしれないリストに親の型のものを追加することができるだろう。したがって、それは許可されていません。
彼らは、ジェネリック医薬品がどのように動作するかにより、互いのサブタイプではありません。何が欲しいのは、このようなあなたの関数を宣言することです。
public void wahey(List<?> list) {}
そして、それは、オブジェクトを拡張するもののリストを受け付けます。あなたも行うことができます:
public void wahey(List<? extends Number> list) {}
これは、あなたが数のサブクラス何かのリストに取るようになる。
私はあなたがモーリス・ナフタリン&フィリップ・ワドラーで「Javaのジェネリックとコレクション」のコピーを拾うお勧めします。
ここでは、抽象化の2次元、リストの抽象化とその内容の抽象化は基本的にあります。それはLinkedListのか、ArrayListのだということを、例えば、言うこと - - それは、リストの抽象化に沿って変化することは完全に罰金ですが、言って、さらに内容を制限するために罰金ではありません:これは(オブジェクトを保持しているリスト)は(リンクリストです)数字のみを保持しています。 (オブジェクトを保持しているリスト)として、それを知っている任意の参照は、それがのいずれかののオブジェクトを保持することができ、その種類の契約で、理解しているので。
それは二重であるかのように、この文字列を扱う:これは、あなたが言った非ジェネリックのサンプルコードでやっていることとは全く異なっています。 (何かを保持しているリスト)として、この(数字のみを保持しているリスト)を処置:かわりに言おうとしています。そして、それはしていません、それはあなたがそれを逃さないように、コンパイラは、それを検出することができます。
あなたは、Javaのジェネリックの主な目的は、コンパイル時の代わりに、実行時に失敗するタイプの非互換性を得ることであることを考えると、「私たちがここでやっていることは本質的です この除いて同じことが、通過します チェックコンパイル時のみで失敗 ランタイム。リストを使用したバージョンはしません コンパイル。 "
あなたが見ているのは完璧な理にかなっています。
ジェネリックへのあなたのための方法を提供します コレクションの種類を伝えます コンパイラに、それができるように、 チェックしました。コンパイラが認識すると コレクションの要素の型、 コンパイラは、あなたが使用していることを確認することができます コレクション一貫してすることができます 値の正しいキャストを挿入 コレクションから取り出されてます。
Javaでは、List<S>
のないのList<T>
がS
のサブタイプであるT
のサブタイプ。この規則は、型の安全性を提供します。
我々はList<String>
がList<Object>
のサブタイプであることを可能にするとしましょう。次の例を考えてみます:
public void foo(List<Object> objects) {
objects.add(new Integer(42));
}
List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);
だから、これは型の安全性を提供しますが、それはまた、欠点を持っている強制的に、それはより柔軟です。次の例を考えてみます:
class MyCollection<T> {
public void addAll(Collection<T> otherCollection) {
...
}
}
ここでは、T
年代のコレクションを持っている、あなたは別のコレクションからすべての項目を追加したいです。あなたはCollection<S>
のS
サブタイプのためのT
でこのメソッドを呼び出すことはできません。あなただけのパラメータコレクションを変更していない、あなたのコレクションに要素を追加しているので、理想的には、これはokです。
この問題を解決するには、Javaは、彼らが「ワイルドカード」と呼んでいます。ワイルドカードは、共分散/ contravarianceを提供する方法です。今考えるワイルドカードを使用して、以下:
class MyCollection<T> {
// Now we allow all types S that are a subtype of T
public void addAll(Collection<? extends T> otherCollection) {
...
otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
}
}
次に、ワイルドカードで我々は、型Tの共分散を可能にし、我々は、(コレクションに項目を追加するなど)安全入力されていない動作をブロックします。この方法で、我々は柔軟性と型の安全性を取得します。