泛型类中的 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<TModel>
将是子类的泛型类型参数的 Class
对象。
也许您在第一个convert
中想要实现的目标应该是:public void convert(String data, Class<? extends TModel> classOfModelType)
但是 TModel 不只是类型的占位符吗?你如何扩展实际上不存在的东西?我承认我是 Java 新手,所以我不确定这是什么,因为我从未见过 '?之前扩展了 TModel'。
【参考方案1】:
这是 Java 中泛型的限制。泛型只存在于编译时,即在运行时对象只是一个BaseConverter
,所以你不能查询它的泛型类型。最简单的解决方案通常是在调用该方法时传入一个Class<TModel>
对象(正如您所做的那样)。如果您不想多次传递,也可以将其传递给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<T>
,它将在运行时变为BaseConverter<Object>
。
但你可以自己保存课程:
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 等价物是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章