泛型与数组
JDK1.5还有一个很重要的设计原则:如果一段代码在编译时系统没有产生:”unchecked未经检测的转换“,则程序在运行时不会引发”ClassCastException“异常。正是基于这个原因,所以数组元素的类型不能包含类型变量或类型形参,除非是无上限的类型通配符。但可以声明这样的数组,即使声明元素类型包含类型变量或类型形参的数组。也就是说:只能声明List<String>[]数组,但不能创建ArrayList<String>[10]这样的数组对象
假设Java能支持创建ArrayList<String>[10]这样的数组对象,则有如下程序
//下面代码实际上是不允许的
List<String>[] lsa=new ArrayList<String>[10];
//强制类型转化为一个Object数组
Object[] oa=(Object[])lsa;
List<Integer> li=new ArrayList<Integer>();
li.add(new Integer(3));
//将List<Integer>对象作为oa的第一个元素
//下面代码没有任何警告
oa[1]=li;
//下面代码也不会有任何警告,但是会引起ClassCastException异常
String s=lsa[1].get(0);
如果能够创建ArrayList<String>[10]这样的数组,经过中间系列的程序运行,势必在代码最后一行引起异常,这就违背Java泛型的设计原则
如果将程序该成如下形式
//下面代码编译时有 “[unchecked]未经检查的转换”警告
List<String>[] lsa=new ArrayList[10];
Object[] oa=(Object[]) lsa;
List<Integer> li=new ArrayList<Integer>();
li.add(new Integer(3));
oa[1]=li;
//下面代码将一起ClassCastException异常
String s=lsa[1].get(0);
上面代码:声明List<String>[]类型的数组变量时允许的;但不允许创建List<String>[]类型的对象,所以创建了一个ArrayList[10]的数组对象,这是允许的。只是把ArrayList<10>对象赋值给List<String>[]变量时会有编译警告:未经检查的转换,即编译器不保证这段代码的类型安全的。所以后面代码会引发ClassCastException异常,但因为编译器 已经提出了警告,所以完全有可能发生这种异常
Java运行创建无上限的通配符泛型数组,例如new ArrayList<?>[10],在这种情况下程序不得不进行强制类型转换,如下代码所示
List<?>[] lsa=new ArrayList<?>[10];
Object[] oa=(Object[]) lsa;
List<Integer> li=new ArrayList<Integer>();
li.add(new Integer(3));
oa[1]=li;
//下面代码将一起ClassCastException异常
String s=lsa[1].get(0);
编译上面代码不会引起任何警告,当程序运行到最后引发ClassCastException异常,因为程序将lsa的第一个元素的第一个集合元素赋值给String类型的变量,所以程序应该自己通过instanceof运算符来保证它的数据类型
List<?>[] lsa=new ArrayList<?>[10];
Object[] oa=(Object[]) lsa;
List<Integer> li=new ArrayList<Integer>();
li.add(new Integer(3));
oa[1]=li;
Object target=lsa[1].get(0);
if(target instanceof String){
String s=(String)target;
}
与此类似的是:创建元素类型是类型变量的数组对象也将导致编译错误。如下面代码所示:
<T> T[] makeArray(Collection<T> coll){
//下面代码将导致编译错误
return new T[coll.size()];
}
因为类型变量在运行时并不存在,所以编译器无法确定实际类型是什么