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