在运行时访问参数化类型信息

Dón*_*nal 11 java generics type-erasure

可能重复:
为什么在运行时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; }

}
Run Code Online (Sandbox Code Playgroud)

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

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

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

Col*_*inD 12

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

public class Foo extends Bar<String> {
}
Run Code Online (Sandbox Code Playgroud)

type参数在编译时String被硬编码Foo.在TypeReference类(以及所有类似的结构,如TypeLiteral在吉斯和TypeToken在GSON),要求你做出一个利用这个事实匿名子类的在你的代码.执行此操作时,将生成包含该信息的实际类文件,就像Foo上面的示例一样.

欲了解更多信息,请参阅尼尔Gafter的博客文章在这里.

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

List<String> foo = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)

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

public <T> T foo(List<T> list)
Run Code Online (Sandbox Code Playgroud)

没有办法找出是什么T.


Sni*_*las 0

我认为这里没有真正的矛盾。

TypeReference 在运行时将被视为遗留代码的原始 TypeReference 类型,但它仍然能够提供有关 SomeType 的信息。类型擦除意味着您不能在运行时使用 TypeReference 的 T,如此处所示。但你仍然可以知道 T 被替换成什么。