问题 在运行时访问参数化类型信息[重复]


可能重复:
为什么在运行时Java中没有删除所有类型信息? 

Java的泛型是通过类型擦除实现的,所以我认为在运行时无法获得有关参数化类型的任何信息。但是,我在杰克逊图书馆找到了以下课程。

(为了这个例子,我稍微简化了这个类)

public abstract class TypeReference<T> {
    final Type _type;

    protected TypeReference() {
        Type superClass = getClass().getGenericSuperclass();
        _type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() { return _type; }

}

该类提供对其参数化类型的访问,如以下测试所示:

void testTypeReference() {
    // notice that we're instantiating an anonymous subclass of TypeReference
    TypeReference<CurrencyDto> tr = new TypeReference<CurrencyDto>() {};
    assert tr.getType() == CurrencyDto.class
} 

这个类说明了可以在运行时检索实际的类型参数(使用反射),这与Java泛型是通过类型擦除实现的概念是如何一致的?


4468
2018-06-09 16:42


起源

我不认为这是另一个问题的重复。那个问题问道 为什么 编译器存储该信息,在此请求解释它如何符合类型擦除的概念。 - ColinD


答案:


具体 类型参数信息在编译时已知存储在类文件中。例如,如果您有一个返回方法的类 List<String> (不 List<T>!)该信息将在运行时可用。同样,如果你有一个班级:

public class Foo extends Bar<String> {
}

类型参数 String 是硬编码的 Foo 在编译时。该 TypeReference class(和所有类似的结构,如 TypeLiteral 在Guice和 TypeToken 在Gson)通过要求你做一个来利用这个事实 匿名子类 它在你的代码中。执行此操作时,将生成具有该信息的实际类文件,就像使用该信息一样 Foo 上面的例子。

有关更多信息,请参阅Neal Gafter的博客文章 这里

类型擦除更多是指您无法在运行时获取有关泛型类型实例的实际类型参数的信息(请注意 Foo,。的子类 Bar<String>,没有类型变量,也不是通用的)。例如,如果您创建泛型类型的实例 ArrayList<E>

List<String> foo = new ArrayList<String>();

没有类型信息存储在该对象中。所以当你把它传递给其他方法时:

public <T> T foo(List<T> list)

没有办法找出什么 T 是。


12
2018-06-09 16:58



如果列表是空的话,说“没有办法找出T是什么,这不是更正确吗?”我觉得有点厚,但我无法真正调和它在运行时如果列表非空,可以通过执行foo.get(0).getClass()获得类型信息。虽然我意识到我们不一定得到T,因为列表中的对象可能是T的子类。 - Marcus Junius Brutus
@Marcus Junius Brutus: T 不一定是第一个元素的类型。例如,第一个元素可以是a Double 而 T 是 Number 要么 Object。另外,请记住,并非每个通用类都是a List 或者你可以获得一个实例的其他容器 T 通过调用方法。 Comparator例如,只有一个方法可以传递实例 T。 - ColinD


这非常简单:您无法从值INSTANCES获取通用信息,但您可以从TYPES(类)获取它,但有一些限制。具体来说,有3个地方可以获得通用类型信息(参见 http://www.cowtowncoder.com/blog/archives/2008/12/entry_126.html 详情);关于超类/接口声明(超类型的参数化),字段声明和方法(参数,返回类型)声明。

在TypeReference的情况下,发生的是你创建一个具有指定超类型的匿名类型;然后,这个信息将被传递,因为传递了匿名类型(类)。


4
2018-06-09 16:58



“真的很简单” - 我不同意你的意见。 - Simon Nickerson
嘿。好吧,VALUE没有泛型类型信息,但类定义很简单。不是整个仿制药,我不会声称。 :)但是FWIW,这个(angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html)可以使人们了解它是如何工作的。 - StaxMan
@StaxMan,谢谢你的回答和有趣的链接。 - Yann-Gaël Guéhéneuc


我觉得这里没有真正的矛盾。

对于遗留代码,TypeReference在运行时将被视为原始TypeReference类型,但它仍然无法提供有关SomeType的信息。什么类型的擦除意味着您不能在运行时使用T of TypeReference 例如这里。但你仍然可以知道T被替换了什么..


0
2018-06-09 16:51