Java 中为什么禁止泛型数组
首先理解两个概念。
1. 共变数组(covariant array)
在 Java 中, array具有共变性。
共变性的意思是说,一个 类型为 T[ ] 的数组可以包含,类型为 T 的元素或者 subtype 类型为 T 的元素。
所以我们可以这样做:
Number[] numbers = newNumber[3]; numbers[0] = newInteger(10); numbers[1] = newDouble(3.14); numbers[2] = newByte(0);
不仅如此,如果 S 是 T 的 subtype, 一个类型为 S[ ] 的数组也是类型 T [ ]数组的 subtype。例如 Integer 是 Number 的 Subtype, 我们可以这么做:
Integer[] myInts = { 1,2,3,4}; Number[] myNumber = myInts;
如果这样做呢? 不通过类型对象直接赋值。
myNumber[0] = 3.14;
上面的代码直接给 类型为T[ ] 的数组赋值, 在编译期间是没有问题的,
但如果运行这段代码,将引发一个错误警告 ArrayStoreException。
这是因为在运行期间,Java 知道这个数组被实例化为整形数组。
2. 类型擦除(type erasure)
在 Java 泛型中,泛型类型是一种类型信息,且在编译之后被编译器丢弃。
所以泛型的类型信息,在运行期间是不存在的。
这个过程叫做「类型擦除」。
//例如: //Java 是一个面对对象语言, 所有的元素在 Java 中都关联着类和对象。 //Java 的 Object 类是所有类的父类。 //所以 𝚂𝚝𝚛𝚒𝚗𝚐[ ] 是 𝙾𝚋𝚓𝚎𝚌𝚝[] 的subtype 但 𝚂𝚝𝚊𝚌𝚔<𝚂𝚝𝚛𝚒𝚗𝚐> 不是 𝚂𝚝𝚊𝚌𝚔<𝙾𝚋𝚓𝚎𝚌𝚝> 的 subtype
另外,泛型没有共变性。
3. 为什么禁止泛型数组?
继续上一个例子:
//因为类型擦除,泛型类型在运行期间,Stack 的 String 类型会被擦除成 Stack。 //所有的 Stack 泛型类型分享同一个 Stack.class //但数组中,不同类型的数组都有自己的 class。 //结合数组的共变性,即便像 1.中的 不通过类型对象直接赋值。 //我们能在编译期间能暂时骗过编译器,但在运行期间,Java 知道我们在做什么, //也就是数组的类型在运行期间不能被擦除,这与泛型的类型擦除相矛盾,所以 Java中禁止了泛型数组。