我有一个界面
interface x {
A getValue();
}
和实施
class y implements x {
public B getValue() { return new B();}
}
B是A的子类。我猜这是因为协变覆盖。
但如果我将界面重写为
interface x{
<T extends A> T getValue();
}
我在实施中收到警告
警告需要未经检查的强制转换 符合A.getValue()
2个版本的界面有什么区别?我以为他们是一样的。
答案 0 :(得分:4)
界面的第二个版本是错误的。它表示getValue
将返回您要求的任何子类 - 返回类型将根据左侧的表达式推断。
因此,如果您获得对x
的引用(让我们称之为obj
),您可以在没有编译器警告的情况下合法地进行以下调用:
x obj = ...;
B b = obj.getValue();
在您的示例中可能不正确,因为如果您添加另一个同时扩展C
的班级A
,您也可以合法地拨打电话:
C c = obj.getValue();
这是因为T
不是属于接口的类型变量,只是属于方法本身。
答案 1 :(得分:3)
基本上当你在做一个<T extends A>
时,你说你想要一个特定的A子类,而不是任何A,因为那样你就可以这样做:
A getValue();
编译器警告您它无法确保返回A的特定定义子类,只返回A本身。
编辑:为了更好地解释这一点,B是A的子类,但不一定是A的唯一子类。请考虑以下层次结构。
B延伸A. C扩展A而不是B
现在我有一个方法:
public void someMethod(x value) {
C c = x.getValue();
}
遵循泛型,这应该编译并保证没有运行时类强制转换异常(这里的接口可能存在其他问题,但肯定有可能用于正确演示这种情况的情况 - 我保持你的例子)。 / p>
但是如果你将y的实例传递给这个方法怎么办?
好吧,现在我得到的是B而不是C.编译器警告你可能会发生这种情况。
答案 2 :(得分:1)
编译器告诉你的是没有办法解决泛型。除了作为输出之外,T没有在任何地方定义 - 你在谈论哪个T?如果接口被广泛化为类型T:
interface x <T extends A> {
T getValue()
}
然后这会起作用,或者如果方法采用了一个值来定义你想要的返回类型:
interface x {
<T extends A> T getValue(T someArgument)
}
或
interface x {
<T extends A> T getValue(Class<T> someArgument)
}
然后就没有警告了。
编辑:请参阅waxwing的帖子,了解为什么不应该使用泛型来解决这个特定问题。我只是展示了泛型是否合适。
答案 3 :(得分:0)
所以,基于AlbertoPL和Yishai的两个答案,试试这个:
class y implements x {
A getValue(){ return new B();}
}
B延伸A,所以没关系。调用者不需要知道返回A的子类,只是它扩展了A.所以我们去了:)