5

私はJava Generics のドキュメントを見ていて、このコードを見つけました。

public class WildcardError {

void foo(List<?> l) {
    //This give a compile time error
    l.set(0,l.get(0));
}
}

から要素を取得し、List<?>それを別の に設定しようとしていることは理解できList<?>ます。したがって、コンパイラはエラーを出します。私の質問は、2 つのリストが異なる場合、つまりl.set(0, m.get(0))ここのリストlと異なる場合に意味があるということmです。しかし、上記の例では、llは同じリストです。コンパイラがそれを理解するほど賢くないのはなぜですか? 実装するのは難しいですか?

編集:ヘルパーメソッドまたはTの代わりに使用して修正できることを認識してい?ます。なぜコンパイラが私のためにそれをしないのか疑問に思っています。

4

3 に答える 3

9

特定のケースでは、これを明示的に修正できます。

public class WildcardError {
    <T> void foo(List<T> l) {
        // This will work
        l.set(0, l.get(0));
    }
}

または、元の API を変更したくない場合は、デリゲート ヘルパー メソッドを導入します。

public class WildcardError {
    void foo(List<?> l) {
        foo0(l);
    }

    private <T> void foo0(List<T> l) {
        // This will work
        l.set(0, l.get(0));
    }
}

<T>残念ながら、コンパイラはその「明白な」型を推測できません。私もそれについて疑問に思っていました。すべてのワイルドカードは非公式に不明な型に変換できるため、コンパイラで改善できるもののようです<T>。おそらく、これが省略された理由はいくつかあります。おそらくこれは単なる直感ですが、形式的には不可能です。

更新

注、私はこの独特の実装を見たばかりですCollections.swap()

public static void swap(List<?> list, int i, int j) {
    final List l = list;
    l.set(i, l.set(j, l.get(i)));
}

JDK 関係者は、これをローカルで処理するために raw タイプに頼っています。これは、これがおそらくコンパイラによってサポートされるべきであることを示す強力なステートメントですが、何らかの理由 (たとえば、これを正式に指定する時間がないなど) で実行されませんでした。

于 2012-07-16T07:45:21.250 に答える
4

l一般に、2 つの式 (この場合はとl) が同じリストを参照しているかどうかを判断する方法がないため、コンパイラはエラーを報告します。

関連する、やや一般化された質問:

于 2012-07-16T07:43:39.503 に答える
3

List<?>は、未知の型の要素を含むリストを意味するため、それを使用して要素を取得したい場合、未知の型のオブジェクトlist.get(i)が返されるため、唯一の有効な推測は になります。次に、それを使用して要素を元に戻そうとすると、コンパイル時エラーが発生します。これは、前述のように不明なタイプしか含めることができないため、それを配置すると が発生する可能性があるためです。Objectlist.set(index, list.get(index))List<?>ObjectClassCastException

これについては、Joshua Bloch の『 Effective Java』第 2 版、項目 28で非常によく説明されています。API の柔軟性を高めるには、境界付きワイルドカードを使用してください。

これは原則としても知られておりPECS、この Q/A に適切な説明があります: PECS (Producer Extends Consumer Super) とは何ですか? (軽微な例外List<?>も同様ですのでご注意ください)List<? extends Object>

簡単に言えば、要素をリストに入れる必要がある場合ではなく、そのメソッド内で要素を取得List<?>する場合にのみ as method パラメータを使用する必要があります。put と getの両方が必要な場合は、Lukas Eder の回答 (タイプ セーフな方法) のように型パラメーターを使用してメソッドを生成するか、単に使用する(タイプ セーフな方法ではない) 必要があります。TList<Object>

于 2012-07-16T07:52:38.080 に答える