泛型的本质:参数类型的应用。将所操作的数据类型定义为一个参数,并在应用的时候指定类型。
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];