要么我使用谷歌太愚蠢,要么到目前为止还没有其他人遇到过这个问题。
我正在尝试编译以下代码:
public interface MyClass {
public class Util {
private static MyClass _this;
public static <T extends MyClass> T getInstance(Class<T> clazz) {
if(_this == null) {
try {
_this = clazz.newInstance();
} catch(Exception e) {
e.printStackTrace();
}
}
return _this;
}
}
}
然后,在“返回_this;”行中我收到错误“类型不匹配:无法从MyClass转换为T” 为什么是这样? T扩展了MyClass,问题出在哪里?如果我将线路改为“return(T)_this;”,我只是得到关于未经检查的演员的警告,但我不喜欢警告;-)有没有办法实现我想要的没有错误或警告?
答案 0 :(得分:6)
想象一下,您有两个MyClass
,Foo
和Bar
的实现。作为MyClass
类型的字段,_this
可以是Foo
或Bar
。
现在,由于您的getInstance
方法返回<T extends MyClass>
,因此以下列方式调用它是合法的:
MyClass myClass = Util.getInstance(MyClass.class);
如果它是第一次调用,则不起作用,因为MyClass
是一个接口,无法使用newInstance()
进行实例化。
Foo foo = Util.getInstance(Foo.class);
Bar bar = Util.getInstance(Bar.class);
现在,如果_this
是Foo
的实例并且您调用了Util.getInstance(Bar.class)
,会发生什么?这就是你不被允许这样做的原因。
答案 1 :(得分:4)
这是因为变量_this
属于MyClass
类型,而不是类型T
。即使它恰好包含T
的实例,编译器也无法知道这一点。
答案 2 :(得分:2)
我刚刚确认这会让编译器感到高兴,并且仍然以您想要的方式约束类型:
public interface MyClass {
public class Util {
private static MyClass _this;
public static MyClass getInstance(Class<? extends MyClass> clazz) {
if(_this == null) {
try {
_this = clazz.newInstance();
} catch(Exception e) {
e.printStackTrace();
}
}
return _this;
}
}
}
考虑客户端代码,这实际上只暴露了这个工厂设计中的一个错误。想象一下:
MyClass foo = MyClass.getInstance(Foo.class); // sets _this to a Foo and returns it
MyClass bar = MyClass.getInstance(Bar.class); // _this is already set to a Foo and
// we return a Foo when we probably
// are expecting a Bar!
答案 3 :(得分:1)
...是由于以下原因:
T
代表MyClass
。getInstance
返回T
MyClass
类型的对象。就像声明一种方法在返回Double
时返回Number
。
...是将return语句更改为
return (T) _this;
(如果你想摆脱警告,请添加@SuppressWarnings("unchecked")
。
正如科林指出:假设你有
class MyClassImpl1 implements MyClass {
}
class MyClassImpl2 implements MyClass {
}
并执行以下操作:
MyClassImpl1 o1 = MyClass.Util.getInstance(MyClassImpl1.class);
// _this now holds a value of type MyClassImpl1...
// ... which causes this line to throw a ClassCastException.
MyClassImpl2 o2 = MyClass.Util.getInstance(MyClassImpl2.class);