泛型的本质:参数类型的应用。将所操作的数据类型定义为一个参数,并在应用的时候指定类型。
1.为什么使用泛型
在JDK1.5之前,泛型程序设计是通过继承来实现的,例如:
List list = new ArrayList(); //当加入或取出元素时,都被当成Object类型来看待 list.add(new Integer(10)); list.add("10");
那么,在取出元素时候,要知道取出元素的类型,并进行强制转换
Integer a = (Integer) list.get(0); //要求强制转换成Integer类型 Integer b = (Integer) list.get(1); //运行时报错 java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
如果使用泛型设计时,编译器进行参数检查
List<Integer> list = new ArrayList<Integer>(); list.add(new Integer(10)); list.add("10"); //编译器报错 list.add(10);
同样,取出时不用进行强制类型转换
Integer a = list.get(0); Integer b = list.get(1);
泛型的优点:让编译器保留参数的类型信息,执行类型检查,执行类型转换操作。 可读性+安全性提升。
2.泛型的类型擦除
在Java源文件编译生成的字节码中是不包含泛型的类型信息的,即泛型只存在编译期。
擦除后将泛型类型替换成限定类型(无限定类型的变量用Object)
List<Integer> list1 = new ArrayList<Integer>(); List<String> list2 = new ArrayList<String>(); System.out.println(list1.getClass() == list2.getClass()); // true System.out.println(list1.getClass().getName()); //java.util.ArrayList System.out.println(list2.getClass().getName()); //java.util.ArrayList
所以,List<Integer> 和 List<String> 使用的是同一份字节码,它们在编译之后都会变成原始类型List。
3.泛型的继承: 不存在任何的继承关系
4.通配符类型
当要遍历一个集合的所有元素时:
void printCollection(Collection c) { Iterator it = c.iterator(); for(int k=0; k<c.size(); k++) { System.out.println(i.next()); } }
但泛型的版本只能接受元素类型为Object类型的集合void printCollection(Collection<Object> c),如果有ArrayList<String> 集合,则编译出错。
在老版本中可以打印任何类型的集合,设计人员设计了通配符"?"来搞定,使新版本也能遍历任何的类型。
void printCollection(Collection<?> c) { for(Object o : c) { //合法,因为存储类型一定是Object的子类型 System.out.println(o); } }
注:不能向Collection<?>容器实例中加入任何非null元素,因为编译器无法确定添加对象的类型
边界通配符: Producer Extends, Consumer Super
上界:ArrayList<? extends Number> collection = null;
匹配Number类以及它的子类
下界:ArrayList<? super Integer> collection = null;
匹配Integer类以及它的超类(Integer Number Object)
5.泛型的方法
拷贝一个数组中所有的对象到集合中的方法
static void fromArraytoCollection(Object[] a, Collection<> c) { for(Object o : a) { c.add(o); //Error } }
使用泛型方法
static <T> void fromArraytoCollection(Object[] a, Collection<T> c) { for(T o : a) { c.add(o); } }
6.泛型的约束和局限性
1) 泛型类的静态字段和静态方法无效、不能实例化对象、instanceOf操作
对 class GenericClass<T>
由于类型的擦除,JVM只有一个GenericClass类,所以GenericClass类的静态字段和静态方法的定义 中不能使用T(多个),T只是与GencricClass实例相关的信息.
T只在编译时被编译器理解,因此也就不能与运行时被JVM理解并执行其代表的操作的操作符(如instanceof 和new)联用
2)不能出创建参数化类型数组
List<Integer>[] ls = new List<Integer>[10]; // error List<?>[] ls = new List<?>[10]; //ok
可以声明一个数组,然后进行强制转换
ls = (List<Integer>[])new List<?>[10];