以下のコードは、Java 7ではコンパイルおよび実行できますが、Java 1.8.0 u25ではコンパイルできません。
public class GenericTest {
public static class GenericClass<T> {
T value;
public GenericClass(T value) {
this.value = value;
}
}
public static class SecondGenericClass<T> {
T value;
public SecondGenericClass(T value) {
this.value = value;
}
}
public static<T >void verifyThat(SecondGenericClass<T> actual, GenericClass<T> matcher) {
}
public static<T >void verifyThat(T actual, GenericClass<T> matcher) {
}
@Test
public void testName() throws Exception {
verifyThat(new SecondGenericClass<>(""), new GenericClass<>(""));
}
}
Java 8のエラーメッセージは次のようになります。
Error:(33, 9) java: reference to verifyThat is ambiguous
both method <T>verifyThat(com.sabre.ssse.core.dsl.GenericTest.SecondGenericClass<T>,com.sabre.ssse.core.dsl.GenericTest.GenericClass<T>) in com.sabre.ssse.core.dsl.GenericTest and method <T>verifyThat(T,com.sabre.ssse.core.dsl.GenericTest.GenericClass<T>) in com.sabre.ssse.core.dsl.GenericTest match
https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2 https://docs.oracle.com/の間のすべての変更を確認しました
javase / specs / jls / se7 / html / jls-15.html#jls-15.12.2
しかし、私はこの振る舞いの正確な理由に気付かなかった。
編集:
いくつかのコメントに答えるだけで、Java 7と8の両方のコンパイラーがそのような呼び出しを処理できることは非常に明らかです(シグニチャーは、コンパイル時の型消去後に残されるものと同様です)。
public static void verifyThat(SecondGenericClass actual, GenericClass matcher) {
}
public static void verifyThat(Object actual, GenericClass matcher) {
}
@Test
public void testName() throws Exception {
verifyThat(new SecondGenericClass<>(""), new GenericClass<>(""));
}
両方のジェネリックメソッドに対して生成され、消去されるバイトコードは同じで、次のようになります。
public static verifyThat(Lcom/sabre/ssse/core/dsl/GenericTest$SecondGenericClass;Lcom/sabre/ssse/core/dsl/GenericTest$GenericClass;)V
public static verifyThat(Ljava/lang/Object;Lcom/sabre/ssse/core/dsl/GenericTest$GenericClass;)V
Edit2:
javac 1.8.0_40でのコンパイルが同じエラーで失敗する
JLS、§15.12.2.5章最も具体的な方法の選択は読みにくいですが、興味深い要約が含まれています。
非公式な直感は、最初のメソッドによって処理された呼び出しをコンパイル時の型エラーなしで他のメソッドに渡すことができる場合、1つのメソッドが別のメソッドよりも具体的であるということです。
次の例を使用して、これを簡単に否定できます。
GenericTest.<String>verifyThat( // invokes the first method
new SecondGenericClass<>(""), new GenericClass<>(""));
GenericTest.<SecondGenericClass<String>>verifyThat( // invokes the second
new SecondGenericClass<>(""), new GenericClass<>(null));
したがって、ここには最も具体的なメソッドはありませんが、例が示すように、引数を使用してどちらのメソッドも呼び出すことが可能で、他のメソッドを適用できなくなります。
Java 7では、(コンパイラの)型引数を見つけてより多くのメソッドを適用できるようにする(限られた型の推論)ため、限られた方法でメソッドを適用不可能にする方が簡単でした。式の引数から推測されnew SecondGenericClass<>("")
た型があり、それだけです。したがって、呼び出しの場合、引数には型があり、そのためメソッドが適用できなくなりました。SecondGenericClass<String>
""
verifyThat(new SecondGenericClass<>(""), new GenericClass<>(""))
SecondGenericClass<String>
GenericClass<String>
<T> void verifyThat(T,GenericClass<T>)
Java 7(さらにはJava 6)のあいまいさを示すあいまいな呼び出しの例があることに注意してください。verifyThat(null, null);
を使用すると、コンパイラエラーが発生しjavac
ます。
しかし、Java 8には呼び出しの適用性の推論(JLS 7とはまったく新しい章があります)があり、コンパイラーはメソッドの候補を適用できる型引数を選択できます(ネストされた呼び出しを通じて機能します)。あなたはあなたの特別な場合のためにそのような型引数を見つけることができます、あなたは両方に合う型引数を見つけることさえできます、
GenericTest.<Object>verifyThat(new SecondGenericClass<>(""), new GenericClass<>(""));
(Java 8では)あいまいさはあいまいですが、Eclipseでも同意しています。対照的に、呼び出し
verifyThat(new SecondGenericClass<>(""), new GenericClass<String>(""));
は、2番目のメソッドを適用不可能にして最初のメソッドを呼び出すのに十分なほど具体的であり、と同様にのタイプnew GenericClass<>("")
が固定されているJava 7で何が起こっているかについてのヒントを与えます。GenericClass<String>
new GenericClass<String>("")
つまり、Java 7からJava 8に(大幅に)変更された最も具体的なメソッドを選択するのではなく、型推論が改善されたことによる適用性です。どちらの方法も適用できるようになると、どちらの方法も他よりも具体的でないため、呼び出しはあいまいになります。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加