Java中泛型方法参数的getClass()方法

10
以下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(); 
2个回答

5

由于 T 类型不是继承自 T,而是继承自 Number,正如你在 <T extends Number> 中声明的一样。就是这么简单。

更好的问题应该是:

Why doesn't the following compile?

<T extends Number> void foo(T t)
{
    Class<T> class1 = t.getClass();
}
答案是因为Object#getClass()返回带有无界通配符?Class<?>,因为对象本身对其期望在任意方法中使用的泛型类型不直接感知。

5

这有点儿傻。对于大多数用例来说,如果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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接