今天我遇到了以下问题。考虑设置:
interface A {
void foo();
}
interface B {
void bar();
}
class Impl implements A,B {
public void foo() { }
public void bar() { }
}
class Usage {
void worksAsParameter(){
acceptIt(new Impl());
}
<T extends A & B> void acceptIt(T foo){
}
<T extends A & B> T returnIt(){
return new Impl(); // <-- Compile error
}
}
代码编译除了标记的最后一个语句外。 Eclipse给了我error: Type mismatch: cannot convert from Impl to T
我的问题是:为什么Impl
在作为参数提供时可分配给T
(显示在worksAsParameter
但不是T
是返回类型时?
另外,null
除了T
之外,Impl
之外的哪个表达式会满足<T extends A & B> T returnIt(){
return new Impl(); // <-- Compile error
}
类型?
请注意,此问题与this SO question不同,尽管类似。
编辑:修正了拼写错误。
===摘要===
我似乎误解了泛型返回类型的工作原理。我会尝试写下我对它的新认识
让我们来看看这个问题:
Usage
我最初的假设是实现类(在这种情况下为T
)决定A
的具体类型,但必须扩展B
和T
。显然,调用者/调用点可以决定Usage
是什么,T
必须提供可分配给T
的值。但是,由于null
是编译时协议,因此除了null
之外不可能提供这样的值(因为它可以分配给任何东西)。
Afaik这意味着表单的任何代码都只能返回<T extends A> T returnIt(){
return x; // <-- Compile error
}
:
{{1}}
一个相当不直观的功能,希望在不同的设置中更有用。谢谢彼得!
答案 0 :(得分:4)
原因
<T extends A & B> T returnIt(){
return new Impl();
}
不编译是T可以是任何扩展A和B的类。你碰巧知道目前只有一个可能的类,但是编译不会“知道”这个。
e.g。
class AB extends A, B { }
Usage usage = ...
AB ab = usage.<AB>returnIt(); // T is AB not Impl.
您可以使用
强制解决问题<T extends A & B> T returnIt(){
return (T) new Impl(); // unchecked cast warning.
}
但是更好的解决方案是
Impl returnIt(){
return new Impl();
}
这定义了两个泛型
<T extends A, B>
T extends A
以及B extends Object
您可能想要的是
<T extends A & B>
其中T必须延伸A和B.
答案 1 :(得分:1)
您可以像以下一样使用它。
<T extends A, B> T returnIt(){
return (T) new Impl(); // <-- cast it to type T
}
答案 2 :(得分:0)
您也可以执行以下操作以避免编译错误。
<T extends A & B> T returnIt(Class<T> type) {
return type.cast(new Impl());
}