代码之家  ›  专栏  ›  技术社区  ›  Lars

为什么所有的类型信息在运行时都不被Java删除?

  •  23
  • Lars  · 技术社区  · 16 年前

    到目前为止,我对Java泛型的明显错误理解是,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 回复  |  直到 13 年前
        1
  •  17
  •   danben    16 年前

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

    事实证明,尽管JVM 不跟踪实际类型参数 对于泛型类的实例,它 跟踪实际类型参数 对于泛型类的子类。在 换言之,当一个新的 ArrayList<String>() 真的只是一个 新的 ArrayList() 在运行时,如果一个类 延伸 ArrayList<String> 然后 JVM知道 String 是实际的 的类型参数 List 的类型 参数。

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

        2
  •  15
  •   axtavt    16 年前

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

    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> ,因此类型参数保留在超类声明中。

        3
  •  3
  •   Ben Lings    16 年前

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

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

    可以使用,但是

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

    不会编译。

    这种技术被称为“超级类型令牌”(请参见 Neal Gafter's article 关于这个问题)。

        4
  •  1
  •   matt b    16 年前

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

        5
  •  0
  •   John Mikic    13 年前

    我无法让参数化类型为我工作(我无法更改超类的代码)。但是,下面的简单代码工作得很好:

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