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

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

import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public abstract class SuperClass { 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(){}; sc.tellMyType(); } } 

执行Main Class导致Hi, my type parameter is class java.lang.Integer

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

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

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

事实certificate,虽然JVM不会跟踪generics类实例的实际类型参数,但它会跟踪generics类的子类的实际类型参数。 换句话说,虽然新的ArrayList()实际上只是运行时的新ArrayList() ,但是如果类扩展了ArrayList ,那么JVM知道StringList的类型参数的实际类型参数。

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

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

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

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

 class Test implements Superclass { // String retained // Accessible via Class.getGenericSuperclass() private List l; // Integer retained (via Field.getGenericType()) public void test(List l) {} // Long retained (via Method.getGenericParameterTypes()) // Character retained (via Method.getGenericReturnType()) public List test() { return null; } } 

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

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

 TypeLiteral> list = new TypeLiteral>() {}; 

可以用,但是

 Class> list = List.class; 

不会编译。

该技术被称为“超级型令牌”(参见Neal Gafter关于该主题的文章 )。

关于类型擦除的常见问题解答可能有所帮助。

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

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