9.6 泛型与数组

9.6 泛型与数组

Java泛型有一个很重要的设计原则—如果一段代码在编译时没有提出“unchecked未经检查的转换”警告,则程序在运行时不会引发ClassCastException异常
正是基于这个原因,所以数组元素的类型不能包含泛型变量或泛型形参,除非是无上限的类型通配符。但可以声明元素类型包含泛型变量或泛型形参的数组。也就是说,只能声明List<Sing>形式的数组,但不能创建ArrayList<String>[10]这样的数组对象

不能创建ArrayList[10]这样的数组对象

反证法

假设Java支持创建ArrayList<String>[10]这样的数组对象,则有如下程序

1
2
3
4
5
6
7
8
9
10
11
// 下面代码实际上是不允许的
List<String>[] lsa= new ArrayList<String>[10];// 1号代码
// 将lsa向上转型为Object[]类型的变量
Object[] oa = lsa;
List<Integer> li = new ArrayList<>();
li.add(3);
// 将List<Integer>对象作为oa的第二个元素
// 下面代码也不会有任何警告,但将引发ClassCastException异常
oa[1] = li;
// 下面代码引起ClassCastException异常
String s = lsa[1].get(0); // 2号代码

在上面代码中,如果1号代码是合法的,经过中间系列的程序运行,势必在2号代码处引发运行时异常,这就违背了Java泛型的设计原则。
如果将程序中的1代码:

1
List<String>[] lsa= new ArrayList<String>[10];// 1号代码

改为:

1
List<String>[] lsa=new ArrayList[10];

其他不变:

1
2
3
4
5
6
7
8
9
//下面代码编译时有“ [unchecked]未经检査的转换”警告
List<String>[] lsa = new ArrayList[10];//1号代码
// 将lsa向上转型为Object[]类型的变量
Object[] oa=lsa;
List<Integer> li = new ArrayList<>();
li.add(3);
oa[1] = li;
// 下面代码引起ClassCastException异常
String s = lsa[1].get(0);//2号代码

上面程序1号代码声明了List<String>类型的数组变量,这是允许的;但不允许创建List<String>囗类型的对象,所以创建了一个类型为ArrayList[10]的数组对象,这也是允许的。
只是把ArrayList[10]对象赋给List<String>口变量时会有编译警告“unchecked]未经检査的转换”’即编译器并不保证这段代码是类型安全的。
上面代码同样会在2号代码引发运行时异常,但因为编译器已经提出了警告,所以完全可能出现这种异常。

可以创建无上限的通配符泛型数组

Java允许创建无上限的通配符泛型数组,例如new ArrayList<?>[10],因此也可以将第一段代码改为使用无上限的通配符泛型数组,在这种情况下,程序不得不进行强制类型转换。正如前面所介绍的,在进行强制类型转换之前应通过instanceof运算符来保证它的数据类型。将上面代码改为如下形式:

1
2
3
4
5
6
7
8
9
10
List<?>[] lsa = new ArrayList<?>[10];
Object[] oa = lsa;
List<Integer> li = new ArrayList<>();
li.add(3);
oa[1] = li;
Object target = lsa[1].get(0);
if (target instanceof String) {
// 下面代码安全了
String s = (String) target;
}

不能创建元素类型是泛型的数组

与此类似的是,创建元素类型是泛型类型的数组对象也将导致编译错误。如下代码所示:

1
2
3
4
<T> T[] makeArray (Collection<T> coll){
//下面代码导致编译错误
return new T [coll.size()];
}