37

次の例では、リストに複数のタイプがある場合は正常にコンパイルされますが、1つの要素がある場合は、割り当て不可能な別のタイプが選択されます。

// compiles fine
List<Class<? extends Reference>> list = Arrays.asList(SoftReference.class, WeakReference.class);
// but take an element away and it no longer compiles.
List<Class<? extends Reference>> list2 = Arrays.asList(WeakReference.class);
// without giving the specific type desired.
List<Class<? extends Reference>> list3 = Arrays.<Class<? extends Reference>>asList(WeakReference.class);

これには論理的な説明があると確信していますが、それは私を免れます。

    Error:Error:line (30)error: incompatible types
required: List<Class<? extends Reference>>
found:    List<Class<WeakReference>>

2つの要素がコンパイルされるのに、1つの要素がコンパイルされないのはなぜですか?

ところで:試してみると、簡単な例を見つけるのは難しいです

List<Class<? extends List>> list = Arrays.asList(ArrayList.class, LinkedList.class);

    Error:Error:line (28)error: incompatible types
required: List<Class<? extends List>>
found:    List<Class<? extends INT#1>>
where INT#1 is an intersection type:
INT#1 extends AbstractList,Cloneable,Serializable

これもコンパイルされません(解析もされません)

List<Class<? extends AbstractList & Cloneable & Serializable>> list = Arrays.asList(ArrayList.class, LinkedList.class);

Error:Error:line (30)error: > expected
Error:Error:line (30)error: ';' expected

しかし、これはうまくコンパイルされます

static abstract class MyList<T> implements List<T> { }
List<Class<? extends List>> list = 
        Arrays.asList(ArrayList.class, LinkedList.class, MyList.class);
List<Class<? extends List>> list = 
        Arrays.<Class<? extends List>>asList(ArrayList.class, LinkedList.class);

編集:マルコの例に基づいています。これらの4つの例では、1つはコンパイルされず、残りは同じタイプの同じリストを生成します。

List<Class<? extends Reference>> list = new ArrayList<>();
list.add(SoftReference.class);
list.add(WeakReference.class);
list.add(PhantomReference.class);

List<Class<? extends Reference>> list = new ArrayList<>(
     Arrays.asList(SoftReference.class));
list.add(WeakReference.class);
list.add(PhantomReference.class);

List<Class<? extends Reference>> list = new ArrayList<>(
     Arrays.asList(SoftReference.class, WeakReference.class));
list.add(PhantomReference.class);

List<Class<? extends Reference>> list = new ArrayList<>(
     Arrays.asList(SoftReference.class, WeakReference.class, PhantomReference.class));
4

4 に答える 4

14

興味深い問題。何が起こっているのかと思います。表示するような2つの要素がある場合、fromの戻り型asListは、すべての引数の中で最も具体的な型であり、最初の例ではList<Reference>です。これは、との割り当て互換性がありList<? extends Reference>ます。引数が1つしかない場合、戻り型は引数の特定の型になります。ジェネリックは共変ではないため、代入互換ではありません。

于 2012-12-30T15:08:31.563 に答える
10

検討

    // ok
    List<Object> list3 = Arrays.asList(new Object(), new String());
    // fail
    List<Object> list4 = Arrays.asList(new String());

List<String>2番目の例では、aをaに割り当てようとしList<Object>ますが、失敗します。

2番目の例は、javacが周囲のコンテキストを調べ、ターゲットタイプを考慮に入れて、T=Objectここで機能すると推測する場合に機能する可能性があります。Java 8はおそらくそれを行うでしょう(私にはわかりません)

1つの状況でのみ、(java 5の)javacは型推論にコンテキスト情報を使用します。http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12を参照してください。 2.8

これを利用して回避策を作成できます

public static <R, T extends R> List<R> toList(T... elements)
{
    return Arrays.asList((R[])elements);
}

これで、コンパイルできます。

    List<Object> list4 = toList(new String());

    List<Class<? extends Reference>> list = toList(SoftReference.class, WeakReference.class);

    List<Class<? extends Reference>> list2 = toList(WeakReference.class);

これはR、引数の型から推測できず、メソッドの結果が割り当てコンテキストにあるため、javacRはターゲットの種類から推測しようとするためです。

これは、割り当てまたはreturnステートメントで機能します

List<Class<? extends Reference>> foo()
{
    return toList(WeakReference.class);  // "subject to assignment conversion"
}

それ以外の場合は機能しません

void bar(List<Class<? extends Reference>> list){...}

bar( toList(WeakReference.class) ); // fail; R not inferred
于 2012-12-30T17:04:37.637 に答える
4

この動作の説明には2つの部分があります。

  1. 引数を変更すると、右側のタイプはどのように変化しますか?
  2. 一部のRHSタイプがLHSタイプと互換性がないのはなぜですか?

1.右側

の署名asList

<T> List<T> asList(T... a)

これは、すべての引数を単一の型Tに統合する必要があることを意味します。これは、すべての引数の型に共通する最も具体的な型です。この特定のケースでは、

asList(WeakReference.class) -> List<Class<WeakReference>>

asList(WeakReference.class, SoftReference.class) 
   -> List<Class<? extends Reference>>

これらは両方とも十分に明白です。

2.左側

では、なぜ型の最初の式を型List<Class<WeakReference>>の変数に割り当てられないのList<Class<? extends Reference>>でしょうか。ルールがそうでなければならない理由を理解するための最良の方法は、矛盾による証明です。次のことを考慮してください。

  • List<Class<? extends Reference>>もっているadd(Class<? extends Reference>)
  • List<Class<WeakReference>>持っていadd(Class<WeakReference>)ます。

ここで、Javaで一方を他方に割り当てることが許可されている場合:

List<Class<WeakReference>> lw = new ArrayList<>();
List<Class<? extends Reference>> lq = lw;
lq.add(PhantomReference.class);

型安全性の明らかな違反になります。

于 2012-12-30T16:32:34.937 に答える
2

これは面白い:

where INT#1 is an intersection type:
INT#1 extends AbstractList,Cloneable,Serializable

たぶんそれが(いくつかの)問題の原因ですか?

要素の交差タイプは一意に決定されない場合があります。独自のリストを宣言するMyList<T> implements List<T>と、配列の交差タイプはとして決定されList<T>ます。

'intersection'タイプを使用Arrays.<Class<? extends List>>asList(ArrayList.class, LinkedList.class);する場合、タイプは明示的に(としてList)記述され、コンパイラーによって推測される必要はありません。

それに加えて、テッド・ホップが言ったことは他のケースでも正しいと思います。

編集:

の違い

List<Class<? extends Reference>> list2 = Arrays.asList(WeakReference.class);

List<Class<? extends Reference>> list3 = Arrays.<Class<? extends Reference>>asList(WeakReference.class);

コンパイラが新しいリストのタイプを決定する時点である可能性があります。割り当てを検討する前に、リストのジェネリックタイプを決定する必要があると思います。このため、割り当てに関係なく、新しいリストのタイプを推測するために必要な情報を取得します。これにより、上記の2つのステートメントによって2つの異なるタイプのリストが作成され、動作が観察される可能性があります。

于 2012-12-30T15:16:36.250 に答える