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

时间:2011-06-09 16:42:23

标签: java generics type-erasure

  

可能重复:
  Why are not all type information erased in Java at runtime?

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泛型是通过类型擦除实现的概念是一致的吗?

3 个答案:

答案 0 :(得分:12)

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

public class Foo extends Bar<String> {
}

类型参数String在编译时被硬编码到FooTypeReference类(以及所有类似的结构,例如Guice中的TypeLiteral和Gson中的TypeToken)通过要求您创建匿名子类来利用此事实它在你的代码中。执行此操作时,将生成包含该信息的实际类文件,就像上面的Foo示例一样。

有关更多信息,请参阅Neal Gafter的博文here

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

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

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

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

没有办法找出T是什么。

答案 1 :(得分:4)

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

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

答案 2 :(得分:0)

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

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