Roh*_*mar 26 java generics type-inference compiler-errors java-8
假设我们有2节课.一个空类Base
,以及该类的子类Derived
.
public class Base {}
public class Derived extends Base {}
Run Code Online (Sandbox Code Playgroud)
然后我们在另一个类中有几个方法:
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");
}
}
Run Code Online (Sandbox Code Playgroud)
这在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
Run Code Online (Sandbox Code Playgroud)
为什么在Java 7中工作,而不是Java 8?怎么可能<T extends Base>
永远匹配收集?
Hol*_*ger 23
问题是类型推断已得到改进.你有一个像这样的方法
public <T extends Base> T get() {
return (T) new Derived();
}
Run Code Online (Sandbox Code Playgroud)
基本上说,"调用者可以决定Base
我返回的子类",这显然是无稽之谈.每个编译器都应该(T)
在这里给你一个关于你的类型转换的未经检查的警告.
现在你有一个方法调用:
set(new Derived(), new Consumer().get());
Run Code Online (Sandbox Code Playgroud)
回想一下,你的方法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());
}
Run Code Online (Sandbox Code Playgroud)
现在调用时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()
Run Code Online (Sandbox Code Playgroud)
问题是:
set(new Derived(), new Consumer().get());
Run Code Online (Sandbox Code Playgroud)
在做的时候new Consumer().get()
,如果看的是签名get
.它返回一个类型T
.我们知道T
不知何故延伸了Base
.但我们不知道T
具体是什么.它可以是任何东西.那么如果我们不能具体决定什么T
,那么编译器怎么样?
告诉编译器的一种方法是通过硬编码并具体告诉它: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());
Run Code Online (Sandbox Code Playgroud)
在Java7(或任何Java版本)中,它将在运行时抛出异常:
线程"main"中的异常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
.
现在你的两个方法都有资格获得第一个参数.根据第二个参数something that extends base
,两者都符合条件,因为某些东西可以派生或扩展派生或扩展集合.这就是Java8抛出错误的原因.
根据Java7,它的类型推断有点弱.所以它尝试做类似的事情,
Base base = new Consumer().get();
set(new Derived(), base);
Run Code Online (Sandbox Code Playgroud)
而且,它找不到合适的方法Derived, Base
作为参数.因此它会抛出错误,但出于不同的原因:
Run Code Online (Sandbox Code Playgroud)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,指出我答案的不完整性.
归档时间: |
|
查看次数: |
5277 次 |
最近记录: |