Java 带擦除的类型推断

Java type inference with erasure

擦除和类型推断有一些问题。我有以下 class 层次结构,看起来并不复杂:

public class Foo<T> {

}

public class Bar<T> {
    private final Class<T> clazz;

    public Bar(Class<T> clazz) {
        this.clazz = clazz;
    }
}

我想做的是这样的:

Bar<Foo<?>> bar = new Bar<>(Foo.class);

这当然行不通,因为 Foo 不完全是 Foo<?>。问题是如何构建这样的Bar?我需要 Bar<Foo<?>>,而不是 Bar<Foo>,因为有一种方法只接受 Bar<Foo<?>> 作为参数。欣赏创意。

抱歉,不存在这样的 Class 对象,因为正如您所指出的,擦除意味着不可能存在。你需要施放它:

((Class<Foo<?>>)(Class)Foo.class)

这将为您提供所需的范围,但可能会生成编译器警告,因为您正在执行未经检查的通用转换。这是合理的:编译器要求您承认您正在抛弃泛型的编译时安全性。但是在这种情况下,这种情况真的不可能产生运行时错误,无论是在你的程序中还是在未来,所以没关系。

必须进行双重转换,因为编译器知道 Foo.class 与 Class> 不兼容,因此您必须先将其转换为 "raw" 类型 Class:在表达式中使用原始类型会禁用编译器对该表达式的泛型类型检查,因此 "impossible" 转换工作正常。

如您的问题 post 所示,您无法在 <> 中提供类型信息的情况下创建通用对象。您必须提供类型或使用 raw 版本。如果你被迫使用通配符类型,那么只需使用原始类型来抑制警告,如下所示

@SuppressWarnings("rawtypes")
Bar<Foo<?>> bar = new Bar(Foo.class);       

//Bar is having no type "<>" (but this is not recommended

由于 java.lang.Class 是不可变的,您可以在 Bar 的构造函数中使用较弱的类型声明:

class Foo<T> {}

class Bar<T> {
    private final Class<? extends T> clazz;

    public Bar(Class<? extends T> clazz) {
        this.clazz = clazz;
    }
}

如果您仍然需要字段的类型为 Class<T> 而不是 Class<? extends T>,则可以安全地将其转换为构造函数。