此程序无法编译:
public class xx {
static class Class1<C> {
void method1(C p) {
}
}
static class Class2<T> extends Class1<Class<? extends T>> {
T object;
void method2() {
this.method1(this.object.getClass());
}
}
}
错误是:
xx.java:10: method1(java.lang.Class<? extends T>) in xx.Class1<java.lang.Class<? extends T>>
cannot be applied to (java.lang.Class<capture#215 of ? extends java.lang.Object>)
this.method1(this.object.getClass());
为什么会这样?为什么编译器似乎认为object.getClass()
返回Class<? extends Object>
而不是Class<? extends T>
?
答案 0 :(得分:4)
代码中的T
没有上限设置,因此? extends T
实际上等同于? extends Object
。就在昨天,我玩了一个类似的例子,并打了这个障碍。我有
static <T> T newInstance(T o) throws Exception {
final Class<? extends T> c = o.getClass();
return c.newInstance();
}
它抱怨同样的错误。考虑一下:Object.getClass()
的返回类型是Class<?>
,编译器希望将?
捕获到具体类型中。但相反,我们不想捕获?
,而是“捕获上限”T
- 而且Java的泛型中没有这样的东西。
答案 1 :(得分:1)
Object.getClass()
被定义为返回Class<? extends |T|>
,其中T
是接收者的静态已知类型(调用对象getClass()
)。请特别注意垂直条erasure operator。类型变量的擦除是其最左边界的擦除。在你的情况下,这是隐式绑定Object
。所以你得到的是Class<? extends Object>
,而不是Class<? extends T>
。
为什么?
想象一下T = List<Integer>
,您可以突然执行以下未经检查的警告:
List<String> myStrings = new ArrayList<>();
List<Integer> myInts = new ArrayList<>();
List<Integer> myIntyStrings = myInts.getClass().cast(myStrings);
myIntyStrings.add(-1);
String myString = myStrings.get(0); // BANG!
但幸运的是我们收到警告..;)
答案 2 :(得分:0)
根据getClass()的文档,返回的对象具有类型Class< ? extends |X| >
,其中|X|
是调用该方法的实例类型的擦除。
因此,对类型为T
的对象调用getClass()会返回Class< ? extends Object >
。我们在此API中没有关于T
的限制信息。
通常,对泛型类使用反射的API要求客户端将类型为Class< T >
的附加参数传递给相关的构造函数或泛型方法。