Java: getClass() of bounded type

2022-09-03 02:40:40

当我在泛型上闲逛时,我注意到了一些东西。在下面的示例中,编译但不编译:doStuff1doStuff2

public <T extends Foo> void doStuff1(T value) {
    Class<? extends Foo> theClass = value.getClass();
}

public <T extends Foo> void doStuff2(T value) {
    Class<? extends T> theClass = value.getClass();
}

因此,我查找了文档并发现了以下内容:Object.getClass()

实际结果类型为“类<?扩展|X|>|十|是调用 getClass 的表达式的静态类型的擦除。

这让我有点好奇。为什么设计成这样?如果适用,我可以理解将类型转换为原始类,但是我没有看到明显的理由为什么它们必须使其也杀死 。它为什么也摆脱它有什么具体的原因,或者它只是一个一般的“让我们摆脱一切,因为它更容易;无论如何,谁会需要它“的方法?getClass()T


答案 1

如果返回,则不会发生任何真正糟糕的事情;实际上,它将帮助很多用例。getClass()Class<? extends X>

唯一的问题是,这在理论上是不正确的。如果一个对象是一个,它不能是 - 没有这样的类,只有一个.ArrayList<String>classClass<ArrayList<String>>Class<ArrayList>

这实际上与擦除无关。如果有一天 Java 得到完全的 reified 类型,应该仍然返回 ;但是应该有一个新方法,例如可以返回更详细的方法。(但是,可能会与许多具有自己方法的现有类发生冲突)getClass()Class<? extends |X|>getType()Type<? extends X>getTypegetType

就时机而言,由于在很多情况下可能有用,我们可以设计自己的方法来做到这一点。Class<? extends X>

static <X> Class<? extends X> myGetClass(X x){ ... }

但这是可以理解的,他们不会把这种黑客放在标准lib中。


答案 2

请考虑以下程序:

var a = new ArrayList<String>();
var b = new ArrayList<Integer>();
var aType = a.getClass();        
var bType = b.getClass();        

if (aType == bType) {
  ...
}

如果我们执行此命令,并且将包含相同的运行时类对象(因为泛型类型的所有实例共享相同的运行时类),并且 if 语句的主体将执行。aTypebType

它也可以很好地编译。特别是,声明的类型和都是 ,并且比较两个兼容类型的引用对编译器是有意义的。aTypebTypeClass<? extends ArrayList>

但是,如果定义为返回,则 的编译时类型将为 ,而 的类型将为 。由于Java将泛型定义为不变的,因此这些是不可转换的类型,因此编译器需要拒绝比较,因为它是荒谬的,即使运行时认为它是真的。getClassClass<? extends T>aTypeClass<? extends ArrayList<String>bTypeClass<? extends ArrayList<Integer>>aType == bType

由于修复此问题需要对java类型系统进行重大扩展,因此声明返回擦除可能被视为更简单的选项,即使它在其他情况下会导致反直觉行为,例如您遇到的问题。当然,一旦以这种方式定义,就无法在不破坏API的情况下对其进行更改...getClass

要解决该变通办法,您可以使用未经检查的强制转换:

@SuppressWarnings("unchecked")
<T> Class<? extends T> getTypedClassOf(T t) {
   return (Class) t.getClass();
}

当然,这意味着您可能需要诉诸未经检查的强制转换,以使编译器了解不可转换类型的类对象可能毕竟是相同的......


推荐