Java设计及的一个原则——如果一段代码在编译时没有提出“unchecked未经检查的转换”的警告,则程序在运行时不会引发ClassCastException异常。正是基于这个原因,所以数组元素的类型不能包含泛型变量、泛型形参,除非时无上限的类型通配符。但可以声明元素类型包含泛型变量或泛型形参的数组。也就是说List<String>[]数组,但不能创建ArrayList<String>[10]这样的数组。
假设Java支持创建ArrayList<String>[10]这样的数组对象,则有以下程序:
//下面代码实际上时不允许的 List<String>[] lsa=new ArrayList<String>[10]; //将lsa向上转型为Object[]类型的变量 Object[] oa=lsa; List<Integer> li=new ArrayList<>(); li.add(3); //将List<Integer>对象作为oa的第二个元素 //下面代码没有任何警告 oa[1]=li; //下面代码也不会出现任何警告,但会引发ClassCastException异常 String s=lsa[1].get(0);
上面代码违背了Java泛型设计的原则。上述过程,图示为:
将程序改成下面形式:
1 import java.util.*; 2 public class GenericAndArray1 3 { 4 public static void main(String[] args) 5 { 6 // 下面代码编译时有“[unchecked] 未经检查的转换”警告 7 List<String>[] lsa = new ArrayList[10]; 8 // 将lsa向上转型为Object[]类型的变量 9 Object[] oa = lsa; 10 List<Integer> li = new ArrayList<>(); 11 li.add(3); 12 oa[1] = li; 13 // 下面代码引起ClassCastException异常 14 String s = lsa[1].get(0); // ① 15 } 16 } 17 ---------- 编译Java ---------- 18 注: GenericAndArray1.java使用了未经检查或不安全的操作。 19 注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。 20 21 输出完成 (耗时 1 秒) - 正常终止
上面代码List<String>[]类型的数组变量是可以的,但是不允许创建new List<String>[]类型的对象,所以创建一个类型为ArrayList[10]的数组对象,这也是允许的。只是把一个ArrayList[10]对象赋给List<String>[]变量时,将会有编译警告:“[unchecked] 未经检查的转换”,即编译器不保证上面这段代码是类型安全的。因此当程序运行到String s = lsa[1].get(0); 将出现运行错误。
Java允许创建无上限的通配符泛型数组,例如new ArrayList<?>[10],因此可以将第一段代码改为使用无上限的通配符泛型数组,在这种情况下,程序不得不进行强制类型转换。下面代码正确:
1 import java.util.*; 2 public class GenericAndArray2 3 { 4 public static void main(String[] args) 5 { 6 7 List<?>[] lsa = new ArrayList<?>[10]; 8 Object[] oa = lsa; 9 List<Integer> li = new ArrayList<>(); 10 li.add(3); 11 oa[1] = li; 12 Object target = lsa[1].get(0); 13 //程序在进行强制类型转换时,需要通过instanceOf运算符判断它的数据类型 14 if (target instanceof String) 15 { 16 // 下面代码安全了 17 var s = (String) target; 18 } 19 } 20 }