不同于其他语言,Java数组具有两点特性:
数组是协变的
如果A是B的子类,那么A[]将是B[]的子类,例如:
Number[] numbers=new Integer[10];
复制代码
Java数组为什么是协变的?在Java的早期版本中,由于没有泛型,而为了能够复用数组的实现,就将数组设计为协变的。但是,如果将数组设计为协变,可能会引发一个问题:类型安全性,例如:
Number[] numbers=new Integer[10];
numbers[0]=new Double(3.14);
复制代码
数组numbers被创建后,Java为其在内存中申请了10个连续的地址空间,并且每个地址空间大小为4字节。而如果将第一个地址空间中放入Double类型的对象,将会引发System.lang.Error进而造成程序崩溃。为了避免出现这种情况,Java为数组引入了第二个特性:数组能够记住元素的类型,并且要进行运行期类型检查。
数组能够记住元素的类型,并且要进行运行期类型检查。
以下代码段,将引发System.lang.ArrayStoreException异常。这是因为数组numbers始终记得它的元素类型是Integer,当对数组进行赋值为Double对象时,Java进行类型检查,发现类型不符,抛出异常。
Number[] numbers=new Integer[10];
numbers[0]=new Double(3.14);
复制代码
为什么不能创建泛型数组?
Java泛型实现依赖于类型擦除。在运行期,泛型类将会被转换为原始类。因此说,Java泛型是不彻底的泛型,仅仅是编译期的泛型。这里主要解释一个问题,为什么不能有泛型数组?由于数组必须进行运行期类型检查,泛型数组也是数组,也要进行运行期类型检查;而由于类型擦除,造成数组运行期类型检查不能正常进行,破坏了Java数组运行期类型检查的机制,故不能使用泛型数组。 那么问题来了,泛型数组就不能使用吗?Java仅仅要求不能创建泛型数组,但却可以使用泛型数组。方法是创建上界数组,再进行强制类型转换,如下:
T extends Comparable<T>
T[] array=(T[])new Comparable[10];
复制代码