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



可能重复:
为什么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泛型通过类型擦除实现的概念如何一致?

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

public class Foo extends Bar<String> {
}

类型自变量CCD_ 3在编译时被硬编码到CCD_。TypeReference类(以及所有类似的构造,如Guice中的TypeLiteral和Gson中的TypeToken)利用了这一事实,要求您在代码中为其创建一个匿名子类。当您执行此操作时,将生成一个包含该信息的实际类文件,就像上面的Foo示例一样。

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

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

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

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

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

没有办法知道T是什么。

这真的很简单:你不能从值INSTANCES中获得通用信息,但你可以从TYPES(类)中获得,但有一些限制。具体来说,有3个地方提供了通用类型信息(请参阅http://www.cowtowncoder.com/blog/archives/2008/12/entry_126.html详细信息);超类/接口声明(超类型的参数化)、字段声明和方法(参数、返回类型)声明。

在TypeReference的情况下,您会创建一个具有指定超类型的匿名类型;并且由于传递了该匿名类型(类),因此该信息将是可用的。

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

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

最新更新