Java泛型:为什么someObject.getClass()不能返回Class<? extends T>?

15

从编译时和运行时角度看,我认为.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>


Class<? super T> type 怎么样? - sjngm
找到一个可能解释它的问题:https://dev59.com/KHVC5IYBdhLWcg3wjyDu - sjngm
@sjngm:1)请看修改,2)给出的其他问题不是那么相关,我(通常)理解superextends之间的区别。 - java.is.for.desktop
4个回答

6

java.lang.Class不能代表一个类型(使用java.lang.reflect.Type来代替)。如果 TArrayList<String>,那么就没有必要有一个 Class<ArrayList<String>>

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

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

等价于:

public static void myMethod(MyClass instance) {

4
根据 getClass方法的Javadoc
实际结果类型为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 的任何子类来说都是正确的,不管传递了什么类实例。

3
我不明白的是为什么X是MyClass而不是T。 - Sergei Tachenov
1
@Sergey Tachenov:因为据我所知,|X|是一种擦除类型,因此不能成为参数化类型。如果它只是T,那么Javadoc就不会明确提到静态类型的擦除 - Sanjay T. Sharma
3
哦,终于明白了。语言规范4.6明确提到:“类型变量(§4.4)的擦除是其左边界的擦除”,在我们这个例子中,就是MyClass。虽然我不太清楚为什么他们要这样做。 - Sergei Tachenov
1
所以基本上这是因为在编译时无法确保强制转换是正确的,因为T的实际类型是未知的?我现在明白了。顺便说一下,请修复您的答案,编辑中缺少一些代码。 - Sergei Tachenov
@SanjayT.Sharma:“之所以使用这个特定的措辞,是因为当你说对于这个类型为T且<T extends MyClass>的变量,有多个类可以扩展MyClass并且满足T extends MyClass的条件。”不,这没有任何意义。该方法是针对“T”泛型的,但对于任何给定的“T”选择,存在一个特定的类型——“T”,问题是为什么在此上调用.getClass()不返回Class<? extends T> - newacct
显示剩余3条评论

2

Java不支持泛型类型<this>,例如:

对象可以实现。

class Object {
    Class<this> getClass()
}

但是,getClass() 方法无法表达它将返回对象的类类型。编译器也无法本地理解这个方法的作用。
在我看来,应该支持这种行为。

但是,getClass()没有办法表达它将返回一个对象的类类型。按照这个逻辑,getClass()也没有办法“表达”它返回Class <? extends(此静态类型的擦除)>,但实际上它就是返回这个类型。 - newacct
@newacct 这是因为它的行为在Java 6的JLS 15.8.2中被定义,这个行为“覆盖”了返回类型。而Java 5.0没有这种行为。 - Peter Lawrey
@PeterLawrey:没错,所以OP的问题是为什么不能用另一种方式定义它。 - newacct
@newacct 没有语法支持 OP 想要的。 - Peter Lawrey
@PeterLawrey:目前没有支持它如何工作的语法。但是它能够工作,因为它在JLS中。这就是我的观点。OP想要的也可能在JLS中。但事实并非如此,这就是OP提出问题的原因。 - newacct
显示剩余4条评论

0

不是

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

这将会工作(尽管它会生成类型安全警告)。


这不会是一个问题,但问题是为什么。有原因吗?此时我们知道实例必须是类T(无论它是什么)或其子类。那么为什么不允许使用? extends T呢? - Sergei Tachenov
@Sergey:原因是,虽然我们知道instance必须是类型为T或其子类,但Object.getClass()并不考虑这一点。请参见我的编辑答案。 - sleske
1
我还是不明白。Javadoc中说:“实际结果类型为Class<? extends |X|>,其中|X|是调用getClass表达式的静态类型的擦除。”在这种情况下,X是T。你是想说X实际上是Object吗?这是因为它是类型参数(因为它适用于泛型方法之外)吗? - Sergei Tachenov
@Sergey:啊,我看到JDK 1.6的javadocs已经扩展了;对于造成的困惑,我很抱歉。 - sleske
根据您的解释,Class<? extends MyClass> 也不应该起作用,因为 Class<? extends Object> 不能隐式转换为 Class<? extends MyClass> - newacct
显示剩余2条评论

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