6

静的メソッドのコンテキストでは、型参照を絞り込み、次のようなオブジェクトのより具体的なメソッドを呼び出したいと思います:

public static <T, L extends List<? extends T> & RandomAccess> void do(L list) {
    // Do some stuff
}

public static <T> void do(Iterable<? extends T> iterable) {
    if(iterable instanceof List && iterable instanceof RandomAccess)
        // invoke do(List&RandomAccess) method
    else
        // do something else
}


したがって、このようなハックを使用するのではなく、do(Iterable)を呼び出すことができる構文があるかどうかを知りたいです:

private static <L extends List<? extends T> & Comparable> L cast(Iterable<? extends T> iterable) {
    return (L) iterable;
}

public static <T> void do(Iterable<? extends T> iterable) {
    if(iterable instanceof List && iterable instanceof RandomAccess)
        do(cast(iterable));
    else
        // do something else


注: この方法でイテラブルをキャストできないことはわかっています

do((List<? extends T> & RandomAccess) iterable);

そして、Lの消去は

L extends List<? extends T> & Comparable

List<? extends T>


では、なぜこの方法でメソッドを呼び出せないのでしょうか?

do((List<? extends T>) iterable); // Which results in invoking do(Iterable<? extends T)
4

3 に答える 3

2

あなたの仮定は正しいです:交差点の消去は最初のタイプです-つまりList(反射などの目的で)。

メソッドタイプとしてキャストする交差タイプを提供することにより、「ハック」なしでコードをコンパイルできます。

public static <T, L extends List<? extends T> & RandomAccess> void method(L list) {
    // do whatever
}

@SuppressWarnings("unchecked") // needed to suppress unsafe cast warning 
public static <T, L extends List<? extends T> & RandomAccess> void method(Iterable<? extends T> iterable) {
    if(iterable instanceof List && iterable instanceof RandomAccess)
        method((L)iterable); // calls the other method
    else
        return; // do whatever
}

このコードがコンパイルされ、必要に応じて 2 番目のメソッドが最初のメソッドを呼び出します。

このテクニックなしで交差点にキャストする方法はありません。


doは Java キーワードであり、有効なメソッド名ではないため、コードはコンパイルされないことに注意してください。method()代わりに、コンパイル可能な例を取得するために使用しました。

RandomAccessまた、あなたがコーディングした場所を意味していたと思いますComparable

于 2013-07-23T14:45:37.000 に答える
1

私の知る限り、の消去は にL extends List<? extends T> & RandomAccessなりますがList、私が間違っていたとしても、それ(List<? extends T>) iterableは要件を満たしていないため使用できませんでした (aListは必ずしもではありませんRandomAccess)。したがって、一致する唯一のメソッドはIterableバージョンになります。

編集:簡単なリフレクションプリントアウトは、私の仮定を確認します。メソッドに名前を付けてmethod(クリエイティブですね?)、次の出力を取得します。

... method(java.util.List)
... method(java.lang.Iterable)

へのキャストで十分だと考えるかもしれませListんが (ジェネリックを無効にするか、リフレクションを介してメソッドを呼び出すと、そうなる可能性があります)、コンパイラは型消去の影響を受けないため、method(java.lang.Iterable)一致のみを認識します。

于 2013-07-23T14:12:28.890 に答える
1

(クリーン ビルドで)静的メソッドの消去されたバージョンを呼び出す方法はないと思います

public static <L extends List<?> & RandomAccess> void do1(L list) {}

private static <L extends List<?> & RandomAccess> L cast(Iterable<?> iterable) 
{
    return (L) iterable;
}

public static void do2(Iterable<?> iterable)
{
    do1(cast(iterable));

    // try to invoke the erased version
    //do1((List)iterable);   // does not compile
}

生の型を介してインスタンスメソッドの消去されたバージョンを呼び出すことができます

static class Helper<T>
{
    <T, L extends List<? extends T> & RandomAccess> void do3(L list)
    {
        do1(list);
    }
}

public static void do2(Iterable<?> iterable)
{
    Helper helper = new Helper(); // raw type
    helper.do3((List) iterable);  // erased method
}

静的メソッドに戻ります。このJava5以前のコードがあるとします

#1 static void foo(List list){}

#2 foo(new ArrayList());

Java5以降、#1は次のように生成されます

#1 static void foo(List<?> list){}

ただし、#2 は raw タイプでそのまま保持されます。なぜ#2はまだコンパイルされるのですか?仕様 (15.12.2.2) では、未チェックの変換を許可することでこれを可能にしています。Foo未チェックの変換は、 rawを に変換できますFoo<..>(ただし、 のような洗練されたものはありませんFoo -> Foo<..>&Bar)。

仕様には、レガシー コードが典型的なジェネリフィケーションに耐えることができることを確認するのに十分なトリックのみが含まれています。あなたのケースは確かに典型的ではなく、トリックは適用されません。

于 2013-07-23T15:25:46.137 に答える