【感谢 @CNife 的指正,下面部分例子已经修正,欢迎大家继续给我挑错 :v】
首先Java号称自己是”类型安全“语言,不会意外怀孕。
Girl g = new Boy(); //编译器爸爸不允许
为了确保类型安全,Java数组就非常龟毛:必须明确知道内部元素的类型,而且会”记住“这个类型,每次往数组里插入新元素都会进行类型检查,不匹配会抛出java.lang.ArrayStoreException。
你可以把数组想像成小鸭子,把它第一眼见到的东西认作妈妈。比如下面这段代码,虽然声明的是Object[]
,但首次实例化成Integer[]
,这个数组小鸭子就记住了Integer
是它妈妈,以后再往里填充String
对象,它就不认了。
Object[] ducks = new Integer[10]; // 小鸭子认定Integer是它妈妈 ducks[0] = "Hello"; // ERROR: ArrayStoreException
但是!泛型有个什么问题呢?就是泛型是用擦除(Erasure)实现的,运行时类型参数会被擦掉。比如下面的例子,无论你声明的的是List<String>
,还是List<Integer>
或者原生类List
,容器实际类型都是List<Object>
。 所以泛型实际上都是狼外婆,它看上去像外婆,但实际上是大灰狼。
// 以下三个容器实际类型都是List<Object> List<String> strList = new ArrayList<String>(); List<Integer> intList = new ArrayList<Integer>(); List rawList = new ArrayList();
所以数组小鸭子遇到泛型狼外婆就要吃苦头了。对数组小鸭子Object[]
来说,GrandMother<RealGrandMother>
和GrandMother<Wolf>
运行时看起来都是GrandMother
。 那小鸭子岂不是要被吃掉? 所以有正义感的程序员哥哥就禁止掉了这件事。
public static void main(String[] args) { GrandMother<RealGrandMother>[] gm = new GrandMother<RealGrandMother>[3]; // 真外婆 Object[] ducks = gm; // 我们告诉数组小鸭子,只有见到外婆才能开门 ducks[0] = new GrandMother<Wolf>(); // 运行时狼外婆看起来和真外婆一模一样 RealGrandMother rgm = ducks[0].get(); // BOOM! 跳出一只狼外婆,小鸭子懵圈了 }
唯一绕过限制,创建泛型数组的方式,是先创建一个原生类型数组,然后再强制转型。
List[] ga = (List<Integer>[])new ArrayList[10];
------------------------以下为原答案,对数组类型的探讨------------------------
Java Language Specification明确规定:数组内的元素必须是“物化”的。
It is a compile-time error if the component type of the array being initialized is not reifiable.
对“物化”的第一条定义就是不能是泛型:
A type is reifiable if and only if one of the following holds:
因为Array的具体实现是在虚拟机层面,嵌地非常深,也查不到源码。只好用javap反编译看看具体初始化数组的字节码。我们反编译下面一段代码:初始化一个String数组和一个int数组。
String[] s=new String[]{"hello"}; int[] i=new int[]{1,2,3};
反编译的片段如下:
Code: 0: iconst_1 1: anewarray #2 // class java/lang/String 4: dup 5: iconst_0 6: ldc #3 // String hello 8: aastore 9: astore_1 10: iconst_3 11: newarray int 13: dup 14: iconst_0 15: iconst_1 ... ...
其中:
anewarray和newarray都是虚拟机内部用来创建数组的命令。最多只能有2的8次方256个操作码,光创建数组就占了不止一个,可见数组的地位有多特殊。
其中newarray用atype来标记数组类型。anewarray用index来标记。从描述里可以看到,数组除了元素类型,还有一个必须确定的是长度,因为数组是一段连续内存。
查一下 Java Virtual Machine 对anewarray命令的描述,
anewarray <type>
<type> indicates what types of object references are to be stored in the array. It is either the name of a class or interface, e.g. java/lang/String, or, to create the first dimension of a multidimensional array, <type> can be an array type descriptor, e.g.[Ljava/lang/String;
比如anewarray字节码命令的格式就是anewarray后面跟一个具体的元素类型。所以不能确定<type>的确切类型,就无法创建数组。