我对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行为所必需的,还是对此效果有任何合理的解释?
答案 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)
{
}