体验泛型
JDK1.5之前的集合类中存在的问题——可以往集合中加入任意类型的对象,例如下面代码:
复制代码
1 package cn.gacl.generic.summary;
2
3 import java.util.ArrayList;
4
5 public class GenericTest {
6
7 public static void main(String[] args) {
8 /**
9 * 不使用泛型之前ArrayList容器可以存储任意类型的对象
10 */
11 ArrayList collection1 = new ArrayList();
12 collection1.add(1);//存储Integer对象
13 collection1.add(1L);//存储Long对象
14 collection1.add("xdp");//存储String对象
15 /**
16 * 这里会报异常: JAVA.LANG.CLASSCASTEXCEPTION:
17 * JAVA.LANG.LONG CANNOT BE CAST TO JAVA.LANG.INTEGER
18 *
19 */
20 int i = (Integer) collection1.get(1);
21 }
22 }
复制代码
JDK1.5之后的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型之外的数据,例如下面的代码:
复制代码
/**
* 使用泛型限定ArrayList容器只能存储字符串类型的对象
*/
ArrayList<String> collection2 = new ArrayList<String>();
collection2.add("孤傲苍狼");
//collection2.add(1);//报错,因为限制了collection2只能存储String类的对象,不能加入Integer类型的对象
//collection2.add(1L);//报错,因为限制了collection2只能存储String类的对象,不能加入Long类型的对象
//由于已经指明集合中存储的是字符串类型的对象,因此这里不用再强制转型了
String element = collection2.get(0);
复制代码
泛型是提供给Javac编译器看的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带参数类型说明的集合时会去去除掉“类型”信息,使程序运行不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样,由于编译生成的字节码会去掉泛型的类型信息,因此只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据。
例如下面的代码就演示了"使用反射得到集合,然后调用add方法往原本只能存储Integer对象的集合中存储一个String类型的对象"
复制代码
1 ArrayList<Integer> collection3 = new ArrayList<Integer>();
2 //对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样
3 System.out.println(collection3.getClass());//结果为:java.util.ArrayList
4 System.out.println(collection3.getClass() == collection2.getClass());//结果为true
5 //使用反射得到集合,然后调用add方法往原本只能存储Integer对象的集合中存储一个String类型的对象
6 collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
7 System.out.println(collection3.get(0));//输出的结果为:abc,这证明字符串对象确实是存储到了原本只能存储Integer对象的集合中
复制代码
备注:
1.泛型是JDK1.5的所有新特性中最难深入掌握的部分,没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中,使用泛型集合,可以将一个集合中的元素限定为一个特定类型,这样集合中就只能存储同一类型的对象,这样更安全;并且当从集合中获取一个对象时,编译器也知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
2.在JDK1.5之后,你还可以按原来的方式将各种不同类型的数据放到同一个集合中,但是编译时会报一个unChecked警告
3.泛型中的类型参数严格说明集合中装载的数据类型是什么和可以加入什么类型的数据,记住:Collection<String>和Collectin<Object>是两个没有转换关系的参数化的类型
二、了解泛型
?ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语: ?整个称为ArrayList<E>泛型类型
?ArrayList<E>中的E称为类型变量或类型参数
?整个ArrayList<Integer>称为参数化类型
?ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
?ArrayList<Integer>中的<>是“typeof”
?ArrayList称为原始类型
?参数化类型与原始类型的兼容性: ?参数化类型可以引用一个原始类型的对象,编译时编译器会报警告,例如:Collection<String> c = new Vector();
?原始类型可以引用一个参数化类型的对象,编译时编译器会报警告,例如:Collection c = new Vector<String>();
?参数化类型不考虑类型参数的继承关系: ?Vector<String> v = new Vector<Object>();//错误,语法上不通过
?Vector<Object> v = new Vector<String>();//错误,语法上不通过
假设Vector<String> v = new Vector<Object>;可以的话,那么以后从v中取出的对象当作String用,而v实际指向的集合中可以加入任意类型的对象,
假设Vector< Object > v = new Vector< String >;可以的话,那么以后可以向v中加入任意类型的对象,而v实际指向的集合中只能装String类型的对象
思考:下面的代码会报错吗?(不会报错)
?Vector v1 = new Vector<String>();//参数化类型的对象可以给原始类型的引用
?Vector<Object> v=v1;//参数化类型的引用可以指向原始类型的对象
三、泛型中的?通配符
问题:定义一个方法,该方法可以打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
错误的定义:
复制代码
1 /**
2 * Collection<Object>中的Object只是说明Collection<Object>实例对象中的方法接收的参数是Object
3 * Collection<Object>是一种具体的类型,new HashSet<Date>也是一种具体的类型,两者没有兼容性问题
4 * @param collection
5 */
6 public static void printCollection(Collection<Object> collection){
7 for(Object obj:collection){
8 System.out.println(obj);
9 }
10 collection.add("abc");//没错
11 collection=new HashSet<Date>();//会报告错误
12 }
|
|