Java Reflection,为什么在运行时保留超类型类型参数

时间:2015-01-20 16:09:28

标签: java generics

因此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

因此类型擦除是为了向后兼容,但为什么保留超类型参数呢?元数据是否已更新以支持它?

为什么是一个而不是另一个?

4 个答案:

答案 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>参数不可恢复时。