问题 Java泛型:为什么someObject.getClass()不返回Class ?


我希望从编译时间以及运行时方面来看,它不会成为问题 .getClass() 提供正确类型的返回值。

但我一定是错的。

public class _GetClassGenerics2 {

  static class MyClass {
  }

  public static void main(String[] args) {
    MyClass myInstance = new MyClass();
    // here it works
    Class<? extends MyClass> type = myInstance.getClass();

    myMethod(myInstance);
  }

  public static <T extends MyClass> void myMethod(T instance) {
    Class<? extends T> type = instance.getClass();
// java.lang.RuntimeException: Uncompilable source code - incompatible types
//  required: java.lang.Class<? extends T>
//  found:    java.lang.Class<capture#1 of ? extends _GetClassGenerics2.MyClass>
  }

}

编辑: 它不起作用 Class<T> 和 Class<? super T> 无论是。


12048
2017-12-16 12:07


起源

怎么样 Class<? super T> type? - sjngm
找到一个可以解释它的问题: stackoverflow.com/questions/252055/java-generics-wildcards - sjngm
@sjngm:1)见编辑,2)给定的其他问题不相关,我(通常)理解之间的区别 super 和 extends。 - java.is.for.desktop


答案:


按照 Javadoc的 getClass 方法

实际的结果类型是 Class<? extends |X|> 其中| X |是擦除   表达式的静态类型   哪个 getClass 叫做。对于   例如,此处不需要强制转换   代码片段

在这里,值为 |X| 在你的代码片段中 MyClass因此 instance.getClass() 只能分配给 Class<? extends MyClass> 要么 Class<?>

这个特定措辞的原因是因为当你说这个变量有类型T在哪里 <T extends MyClass>,可以有多个类扩展 MyClass 因此能够满足 T extends MyClass 标准。没有运行时信息,就无法知道哪个具体的实现子类 MyClass 在方法中通过了。因此,为了提供通用解决方案,它返回 <? extends MyClass> 因为那对于任何子类都适用 MyClass 无论传递什么类实例。


4
2017-12-16 13:45



我没有得到的是为什么X是MyClass,而不是T. - Sergei Tachenov
@Sergey Tachenov:因为AFAICT, |X| 是擦除,因此不能是参数化类型。如果它只是 T,Javadoc不会明确提到 擦除静态类型。 - Sanjay T. Sharma
哦,最后我明白了。语言规范4.6明确地说“类型变量的擦除(§4.4)是其最左边界的擦除”。在我们的案例中,MyClass是哪一个。我不明白为什么他们这样做了。 - Sergei Tachenov
@Sergey:那是因为当你说这个变量有类型时 T 哪里 <T extends MyClass>,可以有多个类扩展 MyClass 因此能够满足 T extends MyClass 标准。没有运行时信息 没有 知道哪个具体实现子类的方式 MyClass 在方法中通过了。因此,为了提供通用解决方案,它返回 <? extends MyClass> 因为对于MyClass的任何子类而言都是如此,无论传递什么类实例。希望澄清它。 - Sanjay T. Sharma
所以基本上是因为在编译时没有办法确保演员是正确的,因为T的实际类型是未知的?我想我现在明白了。顺便说一句,请修复你的答案,编辑中缺少一些代码。 - Sergei Tachenov


java.lang.Class 不代表一种类型(使用 java.lang.reflect.Type 为了那个原因)。如果 T,有人说 ArrayList<String> 那么就没有意义了 Class<ArrayList<String>>

值得注意的是,在这种特殊情况下,该方法不需要是通用的。

public static <T extends MyClass> void myMethod(T instance) {

相当于:

public static void myMethod(MyClass instance) {

6
2017-12-16 13:22





代替

Class<? extends T> type = instance.getClass();

你需要使用

Class<? extends MyClass> type = instance.getClass();

你不能直接使用 T 这里。

原因是方法签名 Object.getClass() (你要调用的)。它的:

public final Class<? extends Object> getClass()

所以你试图转换 Class<? extends Object> 至 Class<? extends T>,这是不允许的(因为你是向下投射)。它  允许使用显式强制转换:

Class<? extends T> type = (Class<? extends T>) instance.getClass();

将起作用(虽然它会产生类型安全警告)。


0
2017-12-16 12:27



这不会是一个问题,但问题是为什么。有原因吗?此时我们知道实例必须是T类(无论是什么)还是其子类。所以为什么 ? extends T 是不允许的? - Sergei Tachenov
@Sergey:原因是,虽然我们知道这一点 instance 必须是类型 T 或子类, Object.getClass() 不考虑这一点。看我编辑的答案。 - sleske
我仍然没有得到它。 javadoc说“实际的结果类型是Class <?extends | X |>,其中| X |是擦除getClass的表达式的静态类型的擦除。”在这种情况下,X是T.你是否想说X实际上是对象?那是因为它是类型参数(因为它在泛型方法之外工作)? - Sergei Tachenov
@Sergey:编译器不关心“实际结果类型” getClass()。编译器执行静态类型检查,因此只关心方法签名。签名说 Class<? extends Object>。如果你 知道 结果类型实际上是方法签名所说的子类,您可以强制转换它,但必须显式转换。 - sleske
“实际结果类型”Javadoc表示“签名的实际结果类型”或“静态返回类型”,因为它还提到不需要显式强制转换(对于实际结果类型),因此它不是真正的答案。在对Sanjay答案的评论中提到了真正的答案。 - Sergei Tachenov


Java不支持通用类型的<this>,例如

对象可以实现

class Object {
    Class<this> getClass()
}

但getClass()无法表达它将返回一个类型,该类型是对象的类。编译器没有本机了解此方法的功能。

恕我直言,应该支持此行为。


0
2017-12-16 12:26



好答案。但请注意,您可以在第一步之前解释第二步。当前的问题是Object.getClass()不返回“正确”类型。你解释一下 为什么 它没有这样做。 - sleske
你能指出任何解释究竟是什么的资源吗? <this>?我在语言规范中找不到它,也无法编译类似的东西。 - Sergei Tachenov
对不起,Java 不 支持“这种”功能?这会很棒,但我想你的意思是 才不是, 对?因为 Class<this> getSomething() 不行。 - java.is.for.desktop
@ java.is.for.desktop感谢您选择,现在修复它。 - Peter Lawrey
“但getClass()无法表达它将返回一个类型,该类型是对象的类。”根据这个逻辑,也没有办法 getClass() “表达”它返回 Class<? extends (the erasure of the static type of this)>,但这实际上是它的回报。 - newacct