ArrayList<E>类定义和ArrayList<Integer>类引用中涉及的术语:
1、整个ArrayList<E>称为泛型类型
2、ArrayList<E>中E称为类型变量或类型参数
3、整个ArrayList<Integer>称为参数化的类型
4、ArrayList<Integer>中的Integer叫类型参数的实例或实际类型参数
5、ArrayList<Integer>中的<>念typeof
6、ArrayList称为原始类型
一、泛型是什么能做什么
泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。
JDK1.5 之前使用集合的问题
1 public static void testJDK5Befor(){ 2 List list=new ArrayList(); 3 list.add(1); 4 list.add(2); 5 list.add("abc"); 6 int a1=(int) list.get(1); 7 int a2=(int) list.get(2); 8 }
执行代码在第7行出现错误 Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
JDK1.5 对集合加入了泛型的概念,直接在编译阶段验证数据类型。
1 public static void testJDK5After(){ 2 List<Integer> list=new ArrayList<Integer>(); 3 list.add(1); 4 list.add(2); 5 list.add("abc"); 6 }
第5行在编写时直接报错,提醒你:The method add(Integer) in the type List<Integer> is not applicable for the arguments (String)
泛型的作用:泛型在编译时期进行严格的类型检查,消除了绝大多数的类型转换。泛型在集合中使用广泛,在JDK1.5之后集合框架就全部加入了泛型支持。在没有使用泛型之前,我们可以往List集合中添加任何类型的元素数据,因为此时List集合默认的元素类型为Object,而在我们使用的时候需要进行强制类型转换,这个时候如果我们往List中加入了不同类型的元素,很容易导致类型转换异常。
二、泛型擦除
泛型只作用于编译阶段,在编译阶段严格检查类型是否匹配,类型检查通过后,JVM会将泛型的相关信息擦出掉(即泛型擦除),也就是说,成功编译过后的class文件中是不包含任何泛型信息的,泛型信息不会进入到运行时阶段。
public static void testMain01(){ ArrayList <String> listStr=new ArrayList<String>(); ArrayList <Integer> listInt=new ArrayList<Integer>(); System.out.println(listInt.getClass()==ArrayList.class);//true System.out.println(listStr.getClass()==listInt.getClass());//true }
通过该例子我们可以看出泛型在编译为.class文件,加载到内存中后是没有泛型概念的。
1、通过泛型的该特性我们可以使用反射绕过泛型检查向集合中添加任意类型的数据
public static void testMain02() throws Exception{ ArrayList <Integer> listInt=new ArrayList<Integer>(); listInt.add(123); //我们要往该集合内添加其他类型的数据 listInt.getClass().getMethod("add", Object.class).invoke(listInt, "string"); System.out.println(listInt.size());//2 System.out.println(listInt.get(1));//string }
三、泛型使用中出现的问题
//参数化类型与原始类型的兼容性-编译警告 Collection<String> = new Vector(); //原始类型可以引用一个参数化类型的对象。 Collection = new Vector<String >(); //参数化类型不考虑类型参数的继承关系 Vector<String> v = new Vector<Object>(); //错! Vector<Object> v = new Vector<String>();//错! //在创建数组实例时,数组的元素不能使用参数化的类型 Vector<Integer> v[] = new Vector<Integer>[10]; //编译可以通过!因为编译器只会按行解释 Vector v1 = new Vector<Integer>(); Vector<Object> v = v1;
四、泛型的?通配符及扩展
<?>可以引用各种参数化的类型,可以调用与参数无关的方法,不能调用与参数有关的方法
/** * 遍历任意集合 * @param collection */ public static void printCollection(Collection<?> collection){ //System.out.println(collection.add("aa")); System.out.println(collection.size()); for(Object obj:collection){ System.out.println(obj); } }
限定通配符的上边界
正确:Vector<? extends Number> v=new Vector<Integer>();表示 该泛型类型必须是number类型或者其子类类型
错误:Vector<? extends Number> v=new Vector<String>();
限定通配符的下边界
正确:Vector<? super Integer> v=new Vector<Number>();表示 该泛型类型必须是Integer类型或者Integer的父类类型
错误:Vector<? extends Integer > v=new Vector<Byte>();
五、定义泛型的方法
//采用自定义泛型的方式打印出任意参数类型中的所有元素 private static<T> void printCollection(Collection<T> collection,T obj2 ){ for(T obj : collection){ System.out.println(obj); } collection.add(obj2); } //定义一个 方法,可以将任意类型的某个数组填充为相应类型的某个对象 private static<T> void fillArray(T[] a,T obj){ for(int i=0;i<a.length;i++){ a[i] = obj; } } //定义一个 方法,自动将Object类型的对象转换成其它类型 private static<T> T autoConvert(Object obj){ return (T)obj; }
六、自定义泛型类
1 package com.jalja.org.base.test; 2 3 import java.util.Date; 4 5 public class GenericDao { 6 public <T> void add(T t){ 7 8 } 9 public <T> T findById(int id){ 10 return null; 11 } 12 13 public static void main(String[] args) { 14 GenericDao gd=new GenericDao(); 15 gd.add(new Date()); 16 String str=gd.findById(12); 17 } 18 }
一般在DAO中,每个DAO操作的是同一个数据类型,而在本例的15行添加date类型,在16行确返回了一个string类型,我们如何控制我们的dao操作的是同一类型呢,使用泛型类
1 package com.jalja.org.base.test; 2 3 import java.util.Date; 4 5 public class GenericDao <T>{ 6 public void add(T t){ 7 System.out.println(t.getClass()); 8 } 9 public T findById(int id){ 10 return null; 11 } 12 13 public static void main(String[] args) { 14 GenericDao<Date> gd=new GenericDao<Date>(); 15 gd.add(new Date()); 16 String str=(String) gd.findById(12); 17 } 18 }
在这里会发现16行出错,Cannot cast from Date to String 。应为你必须操作同一类型 date
泛型类中的静态方法:
package com.jalja.org.base.test; import java.util.Date; public class GenericDao <T>{ public void add(T t){ System.out.println(t.getClass()); } public T findById(int id){ return null; } //泛型中不能出现与泛型类相关的静态方法,应为静态方法是作用于类级别的,与具体类的实例对象没有关系。 //如果使用类名调用,泛型类型参数T是无法确定具体类型的所以,这样不可以。 public static void update(T t){ } //可以定义与泛型类的泛型类型参数无关的静态方法 public static <E> void update(E e){ } }