java泛型中的一个奇怪错误

时间:2011-01-16 03:00:09

标签: java generics

没关系:

Class<? extends String> stringClass = "a".getClass(); 

但这会出错:

<T> void f(T obj) {
    Class<? extends T> objClass = obj.getClass();
}

我知道我可以像:

<T> void f(T obj) {
    @SuppressWarnings("unchecked")
    Class<? extends T> objClass = (Class<? extends T>) obj.getClass();
}

但为什么以前的错误呢?下一版Java 7是否会支持这种用法?

修改

我处理类型擦除没有问题,只要我的代码正确就加上SuppressWarnings,但编译器不支持它。

背后的问题是,SuppressWarnings还不够。当T.getClass() => ? extends T明显直观时,我为什么要压制任何警告?

正如您在javadoc中看到的那样:

  

实际结果类型是Class,其中| X |是擦除调用getClass的表达式的静态类型。

当我们谈论Java泛型时,Object.getClass()是一个特例。它与声明的形式不同,它只返回Classs<?>。我不知道如何实现IDE之类的IDE,在Eclipse IDE中,完成将为您生成Class<? extends |X|>而不是Class<?>。我想在Object类中可能有一些神奇的东西,要制作基类型,哪个?从派生类型扩展,不同。因此,用Java语言编写自己的Object类是不可能的,这将返回? extends |X| |X| this.getClass() Object之类的内容。但Java {{1}}确实如此,因为它是一个特例。

5 个答案:

答案 0 :(得分:2)

Class表示JVM中的类,而不是Java语言。因此,对于List<String>,实际ClassClass<List>。在您的示例中,如果TList<String>,则getClass将返回Class<? extends List>,如Object.getClass API文档中所述。

答案 1 :(得分:1)

这是因为type erasure。在第一种情况下,您手头有一个实际的具体类型,因此.getClass()成功。在第二种情况下,obj的实际类型在运行时被删除,因此.getClass()不能比Object做得更好。

具体化的泛型可以解决这个问题。根据{{​​3}},Java 7中不会出现此功能。该问题也是Alex Miller's blog

答案 2 :(得分:1)

查看the Javadoc,这是Object.getClass()的签名:

public final Class<?> getClass()

接着说

  

实际结果类型是Class&lt;? extends | X |&gt;    其中| X |是擦除   表达式的静态类型   在哪个getClass被调用。

类文字在15.8.2 in the JLS部分中定义。

在第一种情况下,"a".getClass()的静态类型为String,因此getClass()的返回类型为Class<? extends String>。在第二种情况下,obj的删除为Object,因此返回类型为Class<? extends Object>,实际上只是Class<?>

您可能想知道为什么"a".getClass()不会返回Class<String>。毕竟,编译器“知道”“a”是一个String。原因是:当他们Class通用时,他们认为Class<T>应该代表T类型的类,而不是T的某个子类。 getClass的签名属于该决定和继承的后果:

<T> Class<? extends Number> getType(Number obj) {
   return obj.getClass();
}

如果您致电getType(Long.valueOf(123),则会返回Class<? extends Number>。表达式"a"String类型的对象。为保持一致性,"a".getClass()必须返回Class<? extends String>

答案 3 :(得分:1)

getClass的声明返回类型是Class<?>所以,遗憾的是,编译器只知道getClass将返回<? extends Object>的捕获,这与捕获不同<? extends T>

来自javadocs:

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.

这就是具有显式转换的版本的原因。

在第一个示例中,编译器知道对象的静态类型为String,因此可以按预期进行捕获。

请注意,您可以执行以下操作:

public static <T extends Number> void f(T obj) {
    Class<? extends Number> objClass = obj.getClass();
}

因为我们可以保证T的静态类型是Number的子类。

答案 4 :(得分:0)

<? extends T>表示T的特定子类型,

例如,如果T为Exception<? extends T>可以是RuntimeExceptionSQLException。你永远不会知道T.getClass()是RuntimeException还是SQLException。所以需要明确的演员阵容。