泛型
通过前面的学习,了解到集合中可以存储任意类型的对象元素,但是当把一个对象存入集合后,集合会“忘记”这个对象的类型,将该对象从集合中取出时,这个对象的编译类型就统一变成了Object类型。换句话说,在程序中无法确定一个集合中的元素到底是什么类型,那么在取出元素时,如果进行强制类型转换就很容易出错。接下来通过一个案例来演示这种情况,如文件1所示。
文件1 Example23.java
1 import java.util.ArrayList;
2 public class Example23 {
3 public static void main(String[] args) {
4 ArrayList list = new ArrayList();
5 list.add("String"); // 添加字符串对象
6 list.add("Collection");
7 list.add(1); // 添加Integer对象
8 for (Object obj : list) {
9 String str = (String) obj; // 强制转换成String类型
10 System.out.println(str);
11 }
12 }
13 }
运行结果如图1所示。
图1 运行结果
文件1中,向List集合存入3个元素,分别是两个字符串和一个整数。在取出这些元素时,都将它们强转为String类型,由于Integer对象无法转换为String类型,因此在程序运行时会出现如图6-35所示“ClassCastException(类型转换异常)”的错误。为了解决这个问题,在Java中引入了“参数化类型(parameterized type)”这个概念,即泛型。泛型可以限定操作的数据类型,在定义集合类时,可以使用“<参数化类型>”的方式指定该集合中存储的数据类型,具体格式如下:
ArrayList<参数化类型> list = new ArrayList<参数化类型>();
接下类对文件1中的第4行代码进行进行修改,使用泛型来限定ArrayList集合中只能存储String类型的数据,如下所示:
ArrayList<String> list = new ArrayList<String>();
代码修改之后,会发现程序在编译时期就会出现错误提示,如图2所示。
图2 编译错误
在图2中,程序编译报错的原因是修改后的代码限定了集合元素的数据类型,ArrayList<String>这样的集合只能存储String类型的元素,程序在编译时,编译器检查出Integer类型的元素与List集合的规定的类型不匹配,编译不通过,这样就可以在编译时期解决错误,避免程序在运行时期发生错误。
接下来使用泛型再次对文件1进行改写,如文件2所示。
文件2 Example24.java
1 import java.util.ArrayList;
2 public class Example24 {
3 public static void main(String[] args) {
4 // 创建ArrayList集合,使用泛型
5 ArrayList<String> list = new ArrayList<String>();
6 list.add("String"); // 添加字符串对象
7 list.add("Collection");
8 for (String str : list) { // 遍历集合,可以直接指定元素类型
9 System.out.println(str);
10 }
11 }
12 }
运行结果如图3所示。
图3 运行结果
文件2中,使用泛型规定了ArrayList集合只能存入String类型元素,之后向集合中存入了两个String类型元素,并对这个集合进行遍历,从图3可以看出,该文件可以正常运行。需要注意的是,在使用泛型后每次遍历集合元素时,可以指定元素类型为String,而不是Object,这样就避免了在程序中进行强制类型转换。