web-dev-qa-db-ja.com

Java 8:[method]への参照があいまいです

次のコードがJava 7以下で正常にコンパイルされるが、Java 8。

public static void main(String[] args) throws Exception {
    put(get("hello"));
}

public static <R> R get(String d) {
    return (R)d;
}

public static void put(Object o) {
    System.err.println("Object " + o);
}

public static void put(CharSequence c) {
    System.err.println("CharSequence " + c);
}

public static void put(char[] c) {
    System.err.println("char[] " + c);
}

Getメソッドには汎用の戻り型があります。 JDK 7以前では、これは正常にコンパイルされ、Objectパラメーターを使用したputメソッドが選択されます。 JDK 8ではこれをコンパイルできず、putメソッドがあいまいであることを示しています。

どうやらJDK 8はオブジェクトパラメータメソッドをスキップして最後の2つのサブオブジェクトパラメータメソッドを見つけて不平を言っています(つまり、別のパラメータタイプで別のputメソッドを追加すると、コンパイラは新しい最後の2つの方法)

これはバグのようです。

26
Ken

あなたの問題は一般化されたターゲット型推論の副作用で、Java 8。

ターゲット型推論とは

例の方法を見てみましょう。

_public static <R> R get(String d) {
    return (R)d;
}
_

さて、上記のメソッドでは、Rを持つパラメーターがないため、汎用パラメーターRをコンパイラーで解決できません。

そのため、彼らは_Target-type Inference_という概念を導入しました。これにより、割り当てパラメーターに基づいてパラメーターをinferredにできます。

だから、もしそうなら、

_ String str = get("something"); // R is inferred as String here
 Number num = get("something"); // R is inferred as Number here
_

これはJava 7.でうまく機能します。しかし、次のしない

_put(get("something");
static void Put(String str) {} //put method
_

型推論は直接割り当てに対してのみ機能したためです。

直接割り当てがない場合、ジェネリック型はObjectとして推定でした。

したがって、Java 7)でコードをコンパイルすると、put(Object)メソッドは問題なく呼び出されました。

彼らがしたことJava 8

彼らはtype inferenceを改良して、method callsおよびchained method callsから型を推測しました。

それらに関する詳細 here および here

したがって、put(get("something"))を直接呼び出すことができ、ジェネリック型はput()メソッドのパラメーターに基づいてinferredになります。

ただし、ご存じのとおり、メソッドput(Charsequence)およびput(char[])は引数と一致します。そのため、あいまいさがあります。

修正しますか?

コンパイラに必要な情報を正確に伝えるだけで、

_put(TestClass.<CharSequence>get("hello")); // This will call the put(CharSequence) method.
_
30
Codebender

これは既知の非互換性のようです。

この記事 および このバグ の「Area:Tools/javac」セクションを参照してください。

あらすじ

警告付きでJDK 7でコンパイルされた次のコードは、JDK 8ではコンパイルされません。

import Java.util.List;

class SampleClass {

    static class Baz<T> {
        public static List<Baz<Object>> sampleMethod(Baz<Object> param) {
            return null;
        }
    }

    private static void bar(Baz arg) {
        Baz element = Baz.sampleMethod(arg).get(0);
    }
} 

JDK 8でこのコードをコンパイルすると、次のエラーが生成されます。

SampleClass.Java:12: error:incompatible types: Object cannot be converted to Baz
    Baz element = Baz.sampleMethod(arg).get(0);

Note: SampleClass.Java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error 

この例では、未加工の型がsampleMethod(Baz)メソッドに渡されていますが、これはサブタイピングによって適用できます(JLS、Java SE 7 Edition、セクション15.12.2.2を参照)。

メソッドを適用するには未チェックの変換が必要であるため、その戻り値の型は消去されます(JLS、Java SE 7 Edition、セクション15.12.2.6を参照)。この場合、戻り値の型はsampleMethod(Baz)はJava.util.List>ではなくJava.util.Listであるため、get(int)の戻り値の型はObjectであり、Bazとの割り当て互換性はありません。

2
Amila