为什么在运行时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知道String
是List
的类型参数的实际类型参数。
在您的情况下,您正在创建参数化类型的匿名子类,因此保留类型信息。 请参阅文章以获得深入的解释。
类型参数仅从动态类型(即正在创建的对象类型)中删除:
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) { }