泛型类中的 T.class 等价物是啥? [复制]

Posted

技术标签:

【中文标题】泛型类中的 T.class 等价物是啥? [复制]【英文标题】:What's the equivalent of T.class in a generic class? [duplicate]泛型类中的 T.class 等价物是什么? [复制] 【发布时间】:2018-01-11 12:52:28 【问题描述】:

考虑以下通用转换器类(为简洁而简化/不完整)...

public abstract class BaseConverter <TModel>

    public void convert(String data, Class<TModel> classOfModelType)
    
    

    public void convert(String data)
    
        convert(data, TModel.class); // This doesn't work!
    

在子类的外部,我可以毫无问题地调用第一个 convert。考虑使用Car 作为泛型类型参数的这个子类。我可以传入Car.class,像这样……

public class CarConverter extends BaseConverter<Car>

    public void someFunction()
    
        String someString;
        convert(someString, Car.class);
    

我正在尝试做的是尽可能多地移动到基类。由于'Car'在这里是已知的,因此convert 期望Car.class,而TModel 代表基类中的Car(无论如何对于这个具体的BaseConverter),我试图在基类中使用TModel.class,但如前所述,它不起作用。

那么你如何在 Java 中实现这一点?

【问题讨论】:

我认为没有等价物。类型在编译时是未知的,那么编译器会用什么替代?你传入convert 中的类类型,为什么不使用相同的技术呢?向 ctor 添加一个类类型,它会为您提供一个用于转换器的基类。 但仅此而已......它在编译时已知的。例如,如果我在返回 TModel 的 BaseConverter 中添加一个函数,如果您查看子类上的签名,它将返回 Car,因为编译器知道该类型。关键是编译器确实知道传递给它的内容,否则它无法解析任何用法。其他语言允许这样做。猜猜这只是Java的限制。 其实我认为你想要在第二个convert 方法中实现的是第一个convert 完全 所做的。 Class&lt;TModel&gt; 将是子类的泛型类型参数的 Class 对象。 也许您在第一个convert 中想要实现的目标应该是:public void convert(String data, Class&lt;? extends TModel&gt; classOfModelType) 但是 TModel 不只是类型的占位符吗?你如何扩展实际上不存在的东西?我承认我是 Java 新手,所以我不确定这是什么,因为我从未见过 '?之前扩展了 TModel'。 【参考方案1】:

这是 Java 中泛型的限制。泛型只存在于编译时,即在运行时对象只是一个BaseConverter,所以你不能查询它的泛型类型。最简单的解决方案通常是在调用该方法时传入一个Class&lt;TModel&gt; 对象(正如您所做的那样)。如果您不想多次传递,也可以将其传递给BaseConverter 的构造函数。

【讨论】:

这只是令人沮丧,因为正如您所说,在运行时它被编译为 BaseConverter,基本上用 Car 替换了 TModel。换句话说,如果你有四个子类传入四种不同的 TModel 类型,你最终会得到四个具体的类(即所有类型在编译时都是已知的)并且泛型实际上并不存在于任何地方.为什么在这种情况下它无法在编译时获取类,这超出了我的理解,非常令人沮丧。 您最终不会得到 4 个类,而是只有一个类:其中 TModel 被替换为 Object。所以初始类型信息丢失了(实际是erased),无法恢复。 是的,我刚刚读到了。这就是 Java 与泛型是一等公民的其他语言的不同之处。在那里你最终得到了四种新的混凝土类型。当他们被事情的结果宠坏时,他们如何在 Java 中实现它真是令人失望。【参考方案2】:

通过在BaseConverter 构造函数中接受它来存储对具体Class 的引用。像这样的:

public abstract class BaseConverter <TModel>

    private Class<TModel> clazz;

    public BaseConverter(Class<TModel> clazz) 
        this.clazz = clazz;
    

    public void convert(String data, Class<TModel> classOfModelType)
    
    

    public void convert(String data)
    
        convert(data, clazz); 
    


public class CarConverter extends BaseConverter<Car>

    public CarConverter() 
        super(Car.class);
    

    public void someFunction()
    
        String someString;
        convert(someString, Car.class);
    

【讨论】:

虽然我知道我可以做到这一点,并且我很感激我可以在其他 cals 中省略它,但我试图尽可能多地从泛型的类型中推断出来,所以我不必去做。毕竟在编译的时候,它内部会知道它们是 Car 类型,那它怎么可能不知道 Car 类呢?其他语言允许这种事情,所以它看起来是 Java 编译器/语言的限制,而不是泛型本身的设计。 在编译时,在BaseConverter,它只知道泛型类型TModel。为什么编译器会认为TModel = Car?毕竟,CarConverter 并不是唯一可以扩展BaseConverter 类。 但仅此而已...编译器(在其他语言中)不是编译 TModel。他们正在编译 Car 和所有其他实例。如果你创建一个泛型的十个子类,指定十个不同的类型参数,你最终会得到十个具体类型,同样,在其他语言中。我今天才发现 Java 的类型擦除,在我刚才描述的情况下,您只有 一个 类型,而不是十个类型,并且 TModel 本质上更改为 Object。那是丢失的信息。我错误地认为与其他语言一样,Java 做了同样的事情,但事实并非如此。【参考方案3】:

泛型在编译期间被擦除,在运行时不存在,所以T.class 是不可能的。如果您的代码中有BaseConverter&lt;T&gt;,它将在运行时变为BaseConverter&lt;Object&gt;

但你可以自己保存课程:

public abstract class BaseConverter <TModel> 
    private final Class<TModel> clazz;

    protected BaseConverter(Class<TModel> clazz) 
        this.clazz = clazz;
    

    public void someFunction()
    
        String someString;
        convert(someString, clazz); // use class here
    


public class CarConverter extends BaseConverter<Car> 
    public CarConverter() 
        super(Car.class);
    

【讨论】:

以上是关于泛型类中的 T.class 等价物是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

泛型类中的数组

将泛型值推送到 Typescript 中泛型类中的泛型列表

泛型类中的静态方法?

泛型方法或泛型类中的方法是内部调用PInvoke 或是在 COM 导入类中定义的。

JAVA中的泛型类是啥东西?

创建 Java 泛型类时,尖括号中的波浪号是啥意思?