为什么在运行时Java中没有删除所有类型信息?

时间:2010-02-23 18:21:37

标签: java generics type-erasure

我对Java Generics的明显错误理解到目前为止,Type Erasure删除了所有类型信息,以至于在运行时根本没有任何东西。最近我偶然发现了一个代码片段,我不得不问自己:黑客如何做到这一点?简化,它表示为:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public abstract class SuperClass<T> {

    private final Type type;

    protected SuperClass(){
        ParameterizedType parameterizedType =
                (ParameterizedType) getClass().getGenericSuperclass();
        type = parameterizedType.getActualTypeArguments()[0];
    }

    public void tellMyType(){
        System.out.println("Hi, my type parameter is " + type);
    }    
}

public class Example {

    public static void main(String[] args) {
        SuperClass sc = new SuperClass<Integer>(){};
        sc.tellMyType();
    }
}

执行主类会产生Hi, my type parameter is class java.lang.Integer

我们在这里看到的是,T的类型信息也可以在运行时获得,这与我最初的理解相矛盾。

所以我的问题是:为什么编译器保留这个?这是某些内部JVM行为所必需的,还是对此效果有任何合理的解释?

5 个答案:

答案 0 :(得分:18)

来自http://www.artima.com/weblogs/viewpost.jsp?thread=208860

  

事实证明,虽然JVM会   不跟踪实际的类型参数   对于泛型类的实例,它   跟踪实际的类型参数   用于泛型类的子类。在   换句话说,一个新的   ArrayList<String>()真的只是一个   新的ArrayList()在运行时,如果是一个类   扩展ArrayList<String>,然后是。{   JVM知道String是实际的   List类型的类型参数   参数。

在您的情况下,您正在创建参数化类型的匿名子类,因此保留了类型信息。请参阅文章以获得深入的解释。

答案 1 :(得分:15)

仅从动态类型(即正在创建的对象类型)中删除类型参数:

Object o = new ArrayList<String>(); // String erased

它保留在静态类型中(即字段,参数和返回类型,throws子句,超类和超接口声明):

class Test implements Superclass<String> { // String retained 
    // Accessible via Class.getGenericSuperclass()

    private List<Integer> l; // Integer retained (via Field.getGenericType())

    public void test(List<Long> l) {} // Long retained (via Method.getGenericParameterTypes())

    // Character retained (via Method.getGenericReturnType())
    public List<Character> test() { return null; }
}

在您的情况下,您创建了一个SuperClass<Integer>的匿名子类,因此类型参数将保留在超类声明中。

答案 2 :(得分:3)

Google Guice使用它来创建TypeLiteral以在运行时表示泛型类。例如

TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};

可以使用,但

Class<List<String>> list = List<String>.class;

不会编译。

该技术被称为“超级型令牌”(请参阅​​主题上的Neal Gafter's article)。

答案 3 :(得分:1)

This FAQ on type erasure可能会有所帮助。

答案 4 :(得分:0)

我无法让ParameterizedType为我工作(我无法更改SuperClass的代码)。但是,以下简单代码工作正常:

try
    {
    SuperClass<Integer> castSc = (SuperClass<Integer>)sc;
    // Do what you want to do if generic type is Integer...
    }
catch (ClassCastException ignored)
    {
    }