因此java有类型擦除,删除类型参数(在运行时没有泛型类型)。有人可以解释为什么保留超类型参数吗?
class StringList extends ArrayList<String> {
public Type getType() {
Type[] typeArguments = ((ParameterizedType) this.getClass()
.getGenericSuperclass()).getActualTypeArguments();
return typeArguments[0];
}
}
这意味着什么:
new ArrayList<String>()// I cannot use reflection to find out the generified type String
但
new StringList()//I can use reflection to see the generic type String
因此类型擦除是为了向后兼容,但为什么保留超类型参数呢?元数据是否已更新以支持它?
为什么是一个而不是另一个?
答案 0 :(得分:7)
它完成了编译时类型安全性。如果超级StringList
被删除为ArrayList
,则无法防范
StringList stringList = new StringList();
stringList.add(new Integer(123456));
如果类型保留在.class
的{{1}}文件中,编译器可以正确拒绝上面的源代码,因为StringList
不是new Integer(123456)
所期望的类型String
1}}。
答案 1 :(得分:3)
我找不到这个问题的满意答案,但这是我的看法。您的StringList类是一个新类型,是ArrayList的子类。编译器为此新类型生成一个类文件,并添加编译时可用的任何信息。但是,new ArrayList<String>()
不会生成新类型。这是因为您只是实例化现有的泛型类型,并且没有地方存储实例的参数类型。
您看到的其他数据存储在StringList类的Signature属性中。您可以通过在任何编辑器中打开.class文件来查看它。
您的类仍然是类型擦除的,并且您没有运行时保护:
StringList sl = new StringList();
List l = sl;
l.add(new Integer(123)); // no compile or runtime error
更好的答案可能包括如何使用这些附加信息(由应用程序或JVM使用。)
答案 2 :(得分:3)
在Java中,区分编译时类型(如源代码中声明的)和单个对象的运行时类型非常重要。
编译时间类型用于编译和链接(在Java中,链接发生在JVM加载类文件时)。由于您可以编译(并链接)类文件,因此必须在类文件中完整保留编译时类型。
运行时类型用于在运行时进行类型检查,如强制转换或instanceof
指令。也可以使用object.getClass()
查询它们。擦除意味着类型参数不存在于运行时类型中,即JVM不跟踪创建对象的类型参数:
new ArrayList<String>().getClass() == new ArrayList<Integer>().getClass()
也就是说,类型参数在编译时类型中存在(并且可以查询),但在运行时类型中不存在。
答案 3 :(得分:2)
基本上类型定义捕获类型参数。例如:
class StringList extends ArrayList<String> {}
由于<String>
是StringList
定义的一部分,因此可以在运行时恢复。或者,仅仅声明new ArrayList<String>
<String>
参数不可恢复时。