15

Java は、多くの場合、引数に基づいてジェネリックを推論できます (たとえば、C# とは対照的に、戻り値の型に基づいても)。

Pair<T1, T2>適切な例: 値のペアを格納するだけで、次の方法で使用できるジェネリック クラスがあります。

Pair<String, String> pair = Pair.of("Hello", "World");

メソッドofは次のようになります。

public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) {
    return new Pair<T1, T2>(first, second);
}

非常に素晴らしい。ただし、これは、ワイルドカードが必要な次のユースケースでは機能しなくなりました。

Pair<Class<?>, String> pair = Pair.of((Class<?>) List.class, "hello");

List.class(正しい型を作成するための明示的なキャストに注意してください。)

コードは次のエラーで失敗します (Eclipse によって提供されます)。

TestClass.Pair<Class<capture#1-of ?>,String>型の不一致: からに変換できませんTestClass.Pair<Class<?>,String>

ただし、コンストラクターを明示的に呼び出すと、期待どおりに機能します。

Pair<Class<?>, String> pair =
    new Pair<Class<?>, String>((Class<?>) List.class, "hello");

誰かがこの振る舞いを説明できますか? それは設計によるものですか?欲しいですか?私は何か間違ったことをしていますか、それとも設計上の欠陥やコンパイラのバグに遭遇しましたか?

勝手な推測: 「capture#1-of ?」どういうわけか、ワイルドカードがコンパイラによってオンザフライで埋められ、型が aClass<List>になり、変換が失敗することを暗示しているようです (からPair<Class<?>, String>Pair<Class<List>, String>)。これは正しいですか?これを回避する方法はありますか?


Pair完全を期すために、クラスの簡略化されたバージョンを次に示します。

public final class Pair<T1, T2> {
    public final T1 first;
    public final T2 second;

    public Pair(T1 first, T2 second) {
        this.first = first;
        this.second = second;
    }

    public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) {
        return new Pair<T1, T2>(first, second);
    }
}
4

1 に答える 1

12

コンストラクターが機能する理由は、型パラメーターを明示的に指定しているためです。それを行うと、静的メソッドも機能します。

Pair<Class<?>, String> pair = Pair.<Class<?>, String>of(List.class, "hello");

もちろん、最初に静的メソッドを使用する理由は、おそらく型推論を取得するためです (コンストラクターではまったく機能しません)。

ここでの問題は(あなたが示唆したように)、コンパイラがキャプチャ変換を実行していることです。これは[JLSの§15.12.2.6]の結果だと思います:

  • 選択したメソッドの結果の型は、次のように決定されます。
    • 呼び出されるメソッドが void の戻り型で宣言されている場合、結果は void になります。
    • それ以外の場合、メソッドを適用可能にするために未チェックの変換が必要な場合、結果の型はメソッドの宣言された戻り値の型の消去 (§4.6) になります。
    • それ以外の場合、呼び出されるメソッドがジェネリックである場合、1in の場合、Fi をメソッドの正式な型パラメーターとし、Ai をメソッド呼び出しで推論される実際の型引数とし、R を呼び出し対象のメソッドの宣言された戻り値の型とします。呼び出されました。結果の型は、キャプチャ変換 (§5.1.10) を R[F1 := A1, ..., Fn := An] に適用することによって得られます。
    • それ以外の場合、結果の型は、メソッド宣言で指定された型にキャプチャ変換 (§5.1.10) を適用することによって取得されます。

本当に推論が必要な場合、考えられる回避策の 1 つは、次のようにすることです。

Pair<? extends Class<?>, String> pair = Pair.of(List.class, "hello");

変数pairの型が広くなり、変数の型名をもう少し入力する必要がありますが、少なくともメソッド呼び出しでキャストする必要はありません。

于 2009-08-18T15:08:20.730 に答える