【问题标题】:Java generics: why someObject.getClass() doesn't return Class<? extends T>?Java 泛型:为什么 someObject.getClass() 不返回 Class<?扩展 T>?
【发布时间】:2011-05-26 12:36:06
【问题描述】:

我希望从编译时间和运行时间的角度来看,.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&lt;T&gt;Class&lt;? super T&gt;

【问题讨论】:

标签: java class generics types type-safety


【解决方案1】:

Java 不支持 的泛型类型,例如

对象可以实现

class Object {
    Class<this> getClass()
}

但是 getClass() 无法表示它将返回一个类型,即对象的类。编译器也不了解此方法的作用。

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

【讨论】:

  • 好答案。但是请注意,您在第一步之前解释了第二步。直接的问题是 Object.getClass() 没有返回“正确的”类型。你解释为什么它不这样做。
  • 您能否指出任何解释&lt;this&gt; 到底是什么的资源?我在语言规范中找不到它,也无法编译类似的东西。
  • 抱歉,Java 是否支持“this-type”功能?这会很棒,但我猜你的意思是没有,对吧?因为Class&lt;this&gt; getSomething() 不起作用。
  • @java.is.for.desktop 感谢您接收它,现在修复它。
  • "但是 getClass() 无法表示它将返回一个类型,即对象的类。"按照这个逻辑,getClass()也没有办法“表达”它返回Class&lt;? extends (the erasure of the static type of this)&gt;,但实际上它就是这样返回的。
【解决方案2】:

而不是

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

你需要使用

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

这里不能直接使用T

原因是Object.getClass()(您正在调用)的方法签名。它是:

public final Class<? extends Object> getClass()

所以你试图从Class&lt;? extends Object&gt; 转换为Class&lt;? extends T&gt;,这是不允许的(因为你是向下转换的)。它允许使用显式转换的:

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

会起作用(尽管它会生成类型安全警告)。

【讨论】:

  • 这不是问题,但问题是为什么。有原因吗?在这一点上,我们知道实例必须是 T 类(无论是什么)或其子类。那么为什么不允许? extends T 呢?
  • @Sergey:原因是,虽然我们知道instance 必须是T 类型或子类,但Object.getClass() 没有考虑到这一点。请参阅我编辑的答案。
  • 我还是不明白。 javadoc 说“实际结果类型是 Class extends |X|> 其中 |X| 是调用 getClass 的表达式的静态类型的擦除。”在这种情况下,X 是 T。你是想说 X 实际上是 Object 吗?那是因为它是类型参数(因为它在泛型方法之外工作)?
  • @Sergey:编译器不关心getClass() 的“实际结果类型”。编译器进行静态类型检查,因此只关心方法签名。签名上写着Class&lt;? extends Object&gt;。如果您知道结果类型确实是方法签名所说的子类,则可以强制转换它,但必须显式强制转换。
  • “实际结果类型”Javadoc 表示“签名的实际结果类型”或“静态返回类型”,因为它还提到不需要显式转换(实际结果类型),所以它是不是真正的答案。真正的答案在桑杰的答案中提到了。
【解决方案3】:

java.lang.Class 不代表一个类型(使用java.lang.reflect.Type)。如果TArrayList&lt;String&gt;,那么有Class&lt;ArrayList&lt;String&gt;&gt; 是没有意义的。

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

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

相当于:

public static void myMethod(MyClass instance) {

【讨论】:

    【解决方案4】:

    根据the Javadoc of the getClass method

    实际结果类型为Class<? extends |X|> 其中|X|是擦除 表达式的静态类型 在其上调用getClass。为了 例如,这里不需要演员表 代码片段

    这里,您的代码 sn-p 中|X| 的值是MyClass,因此instance.getClass() 只能分配给Class&lt;? extends MyClass&gt;Class&lt;?&gt;

    这个特定措辞的原因是因为当你说这个变量的类型为 T where &lt;T extends MyClass&gt; 时,可以有多个扩展 MyClass 的类,因此能够满足 T extends MyClass 标准。如果没有运行时信息,就无法知道在方法中传递了MyClass 的哪个具体实现子类。因此,为了提供一个通用的解决方案,它返回&lt;? extends MyClass&gt;,因为这对于MyClass 的任何子类都成立,而不管传入的是什么类实例。

    【讨论】:

    • 我不明白为什么 X 是 MyClass,而不是 T。
    • @Sergey Tachenov:因为 AFAICT,|X| 是一种擦除,因此不能是参数化类型。如果只是T,Javadoc 就不会明确提到静态类型的擦除
    • 哦,我终于明白了。语言规范 4.6 明确指出“类型变量的擦除(第 4.4 节)是其最左边界的擦除”。在我们的例子中是 MyClass。不过,我不明白他们为什么这样做。
    • @Sergey:那是因为当你说这个变量的类型为T 其中&lt;T extends MyClass&gt; 时,可以有多个扩展MyClass 的类,因此能够满足T extends MyClass 标准.如果没有运行时信息,则无法知道在方法中传递了MyClass 的哪个具体实现子类。因此,为了提供一个通用的解决方案,它返回&lt;? extends MyClass&gt;,因为这对于 MyClass 的任何子类都适用,而不管传入的是什么类实例。希望能澄清它。
    • 所以基本上是因为在编译时无法确保转换是正确的,因为 T 的实际类型是未知的?我想我现在明白了。顺便说一下,请修正你的答案,编辑中缺少一些代码。
    猜你喜欢
    • 2017-05-21
    • 2021-12-18
    • 2010-11-01
    • 2022-10-07
    • 2020-08-03
    • 1970-01-01
    • 2023-02-02
    相关资源
    最近更新 更多