2つのクラスがあるとしましょう。空のクラスBase
、およびこのクラスのサブクラスDerived
。
public class Base {}
public class Derived extends Base {}
次に、別のクラスにいくつかのメソッドがあります。
import Java.util.Collection
public class Consumer {
public void test() {
set(new Derived(), new Consumer().get());
}
public <T extends Base> T get() {
return (T) new Derived();
}
public void set(Base i, Derived b) {
System.out.println("base");
}
public void set(Derived d, Collection<? extends Consumer> o) {
System.out.println("object");
}
}
これは、Java 7で正常にコンパイルおよび実行されますが、Java 8ではコンパイルされません。エラー:
Error:(8, 9) Java: reference to set is ambiguous
both method set(Base,Derived) in Consumer and
method set(Derived,Java.util.Collection) in Consumer match
なぜJava 7で動作するのに、Java 8では動作しないのですか?どうすれば<T extends Base>
evermatch Collection?
問題は、型推論が改善されたになっていることです。あなたは次のような方法を持っています
_public <T extends Base> T get() {
return (T) new Derived();
}
_
これは基本的に、「呼び出し元はBase
のどのサブクラスを返すかを決定できます」と言っていますが、これは明らかにナンセンスです。すべてのコンパイラは、ここで型キャスト_(T)
_に関するチェックされていない警告を表示する必要があります。
これで、メソッド呼び出しができました。
_set(new Derived(), new Consumer().get());
_
メソッドConsumer.get()
が「呼び出し元は私が返すものを決定できる」と言っていることを思い出してください。したがって、Base
を拡張し、同時にCollection
を実装する型が存在する可能性があると想定するのは完全に正しいです。したがって、コンパイラは「set(Base i, Derived b)
とset(Derived d, Collection<? extends Consumer> o)
のどちらを呼び出すかわからない」と言います。
set(new Derived(), new Consumer().<Derived>get());
を呼び出すことで「修正」できますが、メソッドの狂気を説明するために、次のように変更することもできます。
_public <X extends Base&Collection<Consumer>> void test() {
set(new Derived(), new Consumer().<X>get());
}
_
これで、コンパイラの警告なしにset(Derived d, Collection<? extends Consumer> o)
が呼び出されます。実際の安全でない操作は、get
メソッド内で発生しました。
したがって、正しい修正は、get
メソッドからtypeパラメーターを削除し、それが何を返すかを宣言することです本当に、Derived
。
ちなみに、私を苛立たせているのは、このコードがJava 7でコンパイルできるというあなたの主張です。ネストされたメソッド呼び出しによる型推論が制限されているため、ネストされた呼び出しコンテキストでget
メソッドをBase
Derived
を期待するメソッドに渡すことはできません。結果として、準拠するJava 7コンパイラを使用してこのコードをコンパイルしようとすると失敗しますが、理由は異なります。
そうですね、Java7が喜んで実行できるわけではありません。エラーが発生する前に、いくつかの警告が表示されます。
_jatin@jatin-~$ javac -Xlint:unchecked -source 1.7 com/company/Main.Java
warning: [options] bootstrap class path not set in conjunction with -source 1.7
com/company/Main.Java:19: error: no suitable method found for set(Derived,Base)
set(new Derived(), new Consumer().get());
^
method Consumer.set(Base,Derived) is not applicable
(argument mismatch; Base cannot be converted to Derived)
method Consumer.set(Derived,Collection<? extends Consumer>) is not applicable
(argument mismatch; Base cannot be converted to Collection<? extends Consumer>)
com/company/Main.Java:28: warning: [unchecked] unchecked cast
return (T) new Derived();
^
required: T
found: Derived
where T is a type-variable:
T extends Base declared in method <T>get()
_
問題はこれです:
_set(new Derived(), new Consumer().get());
_
new Consumer().get()
を実行するとき、get
の署名を見ると。タイプT
を返します。 T
がどういうわけかBase
を拡張することはわかっています。しかし、T
が具体的に何であるかはわかりません。何でもかまいません。では、T
が何であるかを具体的に決定できない場合、コンパイラーはどのようにできるでしょうか。
コンパイラーに伝える1つの方法は、ハードコーディングして、set(new Derived(), new Consumer().<Derived>get());
で具体的に伝えることです。
上記の理由は非常に(意図的に繰り返される)危険です。これを実行しようとすると、次のようになります。
_class NewDerived extends Base {
public String getName(){return "name";};
}
NewDerived d = new Consumer().<NewDerived>get();
System.out.println(d.getName());
_
Java7(または任意のJavaバージョン)では、実行時に例外がスローされます。
スレッド「メイン」の例外Java.lang.ClassCastException:com.company.Derivedをcom.company.NewDerivedにキャストできません
get
はタイプDerived
のオブジェクトを返しますが、コンパイラーにそれがNewDerived
であると述べたためです。そして、DerivedをNewDerivedに正しく変換することはできません。これが警告を表示する理由です。
エラーのとおり、new Consumer().get()
の何が問題になっているのかがわかりました。タイプは_something that extends base
_です。 set(new Derived(), new Consumer().get());
を実行すると、引数をDerived (or any super class of it), something that extends Base
として受け取るメソッドが検索されます。
これで、両方のメソッドが最初の引数の対象になります。 2番目の引数_something that extends base
_のように、何かを派生または拡張することができるので、両方とも適格です。これが、Java8がエラーをスローする理由です。
Java7によると、その型推論は少し弱いです。だからそれは似たようなことをしようとします、
_Base base = new Consumer().get();
set(new Derived(), base);
_
また、_Derived, Base
_を引数として取る正しいメソッドを見つけることができません。したがって、エラーがスローされますが、理由は異なります。
_set(new Derived(), new Consumer().get()); ^ method Consumer.set(Base,Derived) is not applicable (argument mismatch; Base cannot be converted to Derived) method Consumer.set(Derived,Collection<? extends Consumer>) is not applicabl e (argument mismatch; Base cannot be converted to Collection<? extends Consu mer>)
_
PS:私の答えの不完全さを指摘してくれたHolgerに感謝します。