2

Javaでこれらの宣言が無効なのはなぜですか?

  1. List<Number> test = new ArrayList<? extends Number>();

  2. List test = new ArrayList<? extends Number>();

インスタンス化中にワイルドカードを使用することは許可されていません。また、ワイルドカードがそれらをメソッドに渡す場合にのみ役立つ場合は?

List<Object> test = new ArrayList<Integer>();ジェネリックは共変で正しくないため、違法ですか?

4

7 に答える 7

3

ワイルドカードのパラメーター化された型のオブジェクトを作成できない理由を理解するには、まず、ワイルドカードのパラメーター化された型の用途を理解する必要があります。

ワイルドカードを使用する理由

ご存じのように、Java ジェネリックは不変です。したがって、型引数が共変であっても、List<Number>a は のスーパークラスではありません。List<Integer>では、異なるオブジェクトを指している同じ参照を持つなど、ジェネリックスでもそのような動作が必要な場合はどうでしょうか? あなたが名前を付けるように、その多形的なもの。、、またはListのリストを参照する単一の参照が必要な場合はどうすればよいでしょうか?IntegerFloatDouble

救助へのワイルドカード:

ワイルドカードを使用すると、上記の動作を実現できます。したがって、 aは a 、などList<? extends Number>を参照できます。したがって、次の宣言が有効です。List<Integer>List<Double>

List<? extends Number> numbers = new ArrayList<Integer>();
numbers = new ArrayList<Double>();
numbers = new ArrayList<Float>();
numbers = new ArrayList<String>();  // This is still not valid (you know why)

では、ここで何を変更したのでしょうか。の参照型だけnumbersです。より強力なコンパイル時チェックを実施するために、ジェネリックが Java に導入されたことに注意してください。したがって、パラメーター化された型の宣言が規則に準拠しているかどうかを判断するのは、主にコンパイラーの仕事です。ワイルドカードがない場合、コンパイラは への参照に対してエラーを表示List<Number>しますList<Integer>

したがって、ワイルドカードは、ジェネリックに共分散のような動作を導入するための単なる方法です。ワイルドカードを使用すると、柔軟性が向上します。つまり、コンパイラが適用する制限が緩和されます。List<? extends Number>参照は、リストがのリストまたは任意のサブタイプを参照できることをコンパイラに伝えます(NumberもちろんNumber、下限のワイルドカードもありますが、それはここでは重要ではありません。それらの違いは、SOのみに関する他の多くの回答ですでに説明されています)

メソッド パラメーターで見られるワイルドカードのパラメーター化された型の主な用途。単一のメソッド パラメーターに対してジェネリック型の異なるインスタンス化を渡したい場合:

// Compiler sees that this method can take List of any subtype of Number
public void print(List<? extends Number> numbers) {
    // print numbers
}

ただし、実行時にオブジェクトを作成するには、具象型を指定する必要があります。ワイルドカード (制限付きまたは制限なし) は具象型ではありません。? extends Numberのサブタイプであるすべてを意味しNumberます。では、 を作成するときに、どのタイプのListが作成されることを期待しますList<? extends Number>か? このケースは、 をインスタンス化できない理由と似ていると考えることができますinterface。それらは単なる具体的なものではないからです。

回避策があります。本当に?

これは違法ですが、 - Java Generics FAQで説明されているように、回避策があることを知って驚かれることでしょう。しかし、私は本当にあなたがそれを必要とするとは思わない.

于 2013-09-04T20:30:54.000 に答える
1

Java ジェネリックは共変ではありません。たとえば、Brian GoetzによるJava の理論と実践: Generics gotchas という記事を参照してください。最初の例には 2 つの問題があります。まず、型をインスタンス化するときは、型を完全に指定する必要があります (型パラメーターを含む)。次に、型パラメーターは左側と正確に一致する必要があります。

型共分散 (またはその欠如) に関しては、これも合法ではありません。

List<Number> test = new ArrayList<Integer>();

Integer延長しているにもかかわらずNumber。これは、2 番目の例が違法である理由も説明しています。生の型は、型パラメーターを にバインドするのとほぼ同じObjectなので、次のようになります。

List<Object> test = new ArrayList<Integer>();

ジェネリックは共変ではないため、これも失敗します。

型パラメーターを完全に指定する必要がある理由については、Java 言語仕様の §8.1.2で概念が説明されています。

ジェネリック クラス宣言は、型引数による型パラメーター セクションの可能な呼び出しごとに 1 つずつ、パラメーター化された型のセット (§4.5) を定義します。

実際の型のみをインスタンス化できます。ジェネリック型の型パラメーターがバインドされていない限り、ジェネリック型自体は不完全です。(ジェネリック クラスによって定義されたセットの中で) どの特定のパラメーター化された型がインスタンス化されているかをコンパイラーに伝える必要があります。

ジェネリックが共変でない理由については、これは次の種類のエラーを防ぐことを目的としていました。

List<Integer> iTest = new ArrayList<Integer>();
List<Number> test = iTest;
test.add(Double.valueOf(2.5));
Integer foo = iTest.get(0); // Oops!
于 2013-09-04T20:08:41.480 に答える