以下Java方法无法编译:
<T extends Number> void foo(T t)
{
Class<? extends T> klass = t.getClass();
}
收到的错误是:
类型不匹配:无法从Class<capture#3-of ? extends Number>
转换为Class<? extends T>
有人可以解释为什么Class<? extends T>
无效,但Class<? extends Number>
没问题吗?
Javadoc说:
实际结果类型为
Class<? extends |X|>
,其中| X |是调用getClass的表达式的静态类型的擦除。例如,此代码片段中不需要强制转换:
Number n = 0;
Class<? extends Number> c = n.getClass();
答案 0 :(得分:5)
因为T
类'类型未从T
延伸。相反,它从Number
延伸,正如您在<T extends Number>
中声明的那样。就那么简单。
更好的问题是:
为什么不进行以下编译?
<T extends Number> void foo(T t) { Class<T> class1 = t.getClass(); }
答案是Object#getClass()
返回带有无界通配符Class<?>
的{{1}},因为对象本身并不直接了解其在任意方法中所期望的泛型类型
答案 1 :(得分:3)
有点傻。对于大多数用例,如果x.getClass()
返回Class<? extends X>
而不是删除Class<? extends |X|>
,那会更好。
擦除是导致信息丢失的原因,使您的代码显然是安全的,无法编译。 t.getClass()
会返回Class<? extends |T|>
和|T| = Number
,因此会返回Class<? extends Number>
。
擦除(由语言规范强制要求)是为了保持理论上的正确性。例如
List<String> x = ...;
Class<List> c1 = x.getClass(); // ok
Class<List<String>> c2 = x.getClass(); // error
尽管c2似乎非常合理,但在Java中,List<String>
实际上没有这样的类。 List
只有一个班级。所以允许c2在理论上是不正确的。
这种形式在现实世界的用法中产生了许多问题,程序员可以推断Class<? extends X>
对于他们的目的是安全的,但必须处理已删除的版本。
您只需定义自己的getClass
即可返回未删除类型
static public <X> Class<? extends X> getFullClass(X x)
return (Class<? extends X>)(Class) x.getClass() ;
<T extends Number> void foo(T t)
{
Class<? extends T> klass = getFullClass(t);
}