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



以下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(); 

因为T类的类型不从T扩展。相反,它从Number扩展,正如您在<T extends Number>中声明的那样。就是这么简单。

一个更好的问题应该是:

为什么下面的代码不能编译?

<T extends Number> void foo(T t)
{
    Class<T> class1 = t.getClass();
}

答案是Object#getClass()返回Class<?>和一个无界通配符?,因为对象本身并不直接意识到它的泛型类型,这是在任意方法中所期望的。

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

相关内容

  • 没有找到相关文章