java基础——泛型(jdk1.5新特性)

来源:互联网 发布:网易房产数据 上海 编辑:程序博客网 时间:2024/05/18 18:20

五、泛型

(一)泛型认识

1、Jdk 1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据

ArrayList<Integer> collection2 = new ArrayList<Integer>(); //<>明确指出collection2的类型为Integer,更安全collection2.add(1);/*collection2.add(1L);collection2.add(“abc”);*///这两行代码编译时就报告了语法错误int i2 = collection2.get(0);//不需要再进行类型转换

注:没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。

2、泛型术语
Ø整个称为ArrayList<E>泛型类型
ØArrayList<E>中的E称为类型变量或类型参数
Ø整个ArrayList<Integer>称为参数化的类型
ØArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ØArrayList<Integer>中的<>念着typeof
ØArrayList称为原始类型


例如:
二)泛型内部原理及更深应用
1、原理:泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,
编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛
型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信
息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再
调用其add方法即可。
例如:
ArrayList<Integer> constructor1 = new ArrayList<Integer>();//实例化限定类型为Integer的集合constructor1.getClass().getMethod("add", Object.class).invoke(constructor1, "abc");//使用反射,跳过编译器,输入String类型的信息 System.out.println(constructor1.get(0));//打印结果为abc

2、参数化类型与原始类型的兼容性:

1)参数化类型可以引用一个原始类型的对象,编译报告警告,例如, Collection<String> c = new Vector();//可不可以,不就是编译器一句话的事吗?
2)原始类型可以引用一个参数化类型的对象,编译报告警告,例如, Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去

3、参数化类型不考虑类型参数的继承关系:

1)Vector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯
2)Vector<Object> v = new Vector<String>(); //也错误!

注:泛型中的类型参数严格说明集合中装载的数据类型是什么和可以加入什么类型的数据,记住:Collection<String>和Collection<Object>是两个没有转换关系的参数化的类型。
  假设Vector<String> v = new Vector<Object>();可以的话,那么以后从v中取出的对象当作String用,而v实际指向的对象中可以加入任意的类型对象;假设Vector<Object> v = new Vector<String>();可以的话,那么以后可以向v中加入任意的类型对象,而v实际指向的集合中只能装String类型的对象。
3)编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:

Vector<Integer> vectorList[] = new Vector<Integer>[10];

思考题:下面的代码会报错误吗?
Vector v1 = new Vector<String>(); 
Vector<Object> v = v1;

答:不会报错


(三)泛型中的?通配符

1、问题:
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
错误方式:

public static void printCollection(Collection<Object> cols) {for(Object obj:cols) {System.out.println(obj);}/* cols.add("string");//没错 cols = new HashSet<Date>();//会报告错误!*/}

正确方式:

public static void printCollection(Collection<?> cols) {for(Object obj:cols) {System.out.println(obj);}//cols.add("string");//错误,因为它不知自己未来匹配就一定是Stringcols.size();//没错,此方法与类型参数没有关系 cols = new HashSet<Date>();}
总结:
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。

/*Cols<Object> 中的Object只是说明Cols<Object> 实例对象中的方法接受的参数是ObjectCols<Object> 是一种具体类型,new HashSet<Date>也是一种具体类型,两者没有兼容性问题。*/
Collection<?>  a可以与任意参数化的类型匹配,但到底匹配的是什么类型,只有以后才知道,所以,a=new ArrayList<Integer>和a=new ArrayList<String>都可以, 但a.add(new Date())或a.add(“abc”)都不行,


2、泛型中的?通配符的扩展

1)限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
2)限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new
Vector<Byte>();
3)提示:
限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值
Vector<? extends Number> y = new Vector<Integer>();
Vector<Number> x = y;
上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,
只能通过强制类型转换方式来赋值。

(四)泛型集合的综合应用案例

1、对HasMap的迭代

HashMap<String,Integer> hm = new HashMap<String,Integer>();  hm.put("zxx",19);  hm.put("lis",18);    Set<Map.Entry<String,Integer>> mes= hm.entrySet();//转换成Set方法来进行迭代,Entry类似javabean有set和get方法  for(Map.Entry<String,Integer> me : mes) {   System.out.println(me.getKey() + ":" + me.getValue());  }

打印:

lis:18
zxx:19

2 对在jsp页面中也经常要对Set或Map集合进行迭代:

<c:forEach items=“${map}” var=“entry”>${entry.key}:${entry.value}</c:forEach>

(五)自定义泛型

1、编写一个泛型方法,自动将Object类型的对象转换成其他类型。

public static <T> T autoConvert(Object obj){return (T)obj;}

在main:

Object obj = "abcd";  String autoConvert = autoConvert(obj);System.out.println(autoConvert);





原创粉丝点击