Java基础加强第五讲 泛型
大家可以看看JDK帮助文档中的Class的定义,其中好多地方涉及到了<>
的语法形式,这就是我们今天要讲解的泛型。
泛型是JDK1.5的所有新特性中最难深入掌握的部分,不过,我们在实际应用中不能掌握得那么深入,掌握泛型中一些最基本的内容就差不多了。
体验泛型
没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
JDK1.5以前的集合类中存在什么问题呢?JDK1.5以前,对象保存到集合中就会失去其特性,取出时通常要程序员手工进行类型的强制转换,这样不可避免就会引发程序的一些安全性问题。试看以下代码:
JDK1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据,试看如下代码:
package cn.liayun.generic;
import java.util.ArrayList;
public class GenericTest {
public static void main(String[] args) throws Exception {
ArrayList<String> collection2 = new ArrayList<String>();
collection2.add("abc");
}
}
那么,这两行代码编译时就报告了语法错误:
而且,以下代码不再需要进行类型转换:
在JDK1.5中,你还可以按原来的方式将各种不同类型的数据装到一个集合中,但编译器会报告unchecked警告。
引入泛型以后,我之前讲解反射的代码例子,即用反射的方式通过String(StringBuffer buffer);
构造方法创建String类的实例,就可以改写成如下形式了,这种情况下创建实例对象时不需要类型转换。
package cn.liayun.generic;
import java.lang.reflect.Constructor;
public class GenericTest {
public static void main(String[] args) throws Exception {
Class<String> clazzString1 = String.class;
Constructor<String> constructor1 = clazzString1.getConstructor(StringBuffer.class);
String str2 = constructor1.newInstance(new StringBuffer("abc"));//不用进行类型转换了。
System.out.println(str2.charAt(2));//c
}
}
我是怎么知道什么情况下可以用泛型的呢?这要看类的定义,只有类被定义成了泛型,才可以对其进行参数化应用。
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。通过下面的代码验证getClass()方法返回的结果已经去掉了“类型”信息。
package cn.liayun.generic;
import java.util.ArrayList;
public class GenericTest {
public static void main(String[] args) throws Exception {
ArrayList<String> collection2 = new ArrayList<String>();
ArrayList<Integer> collection3 = new ArrayList<Integer>();
System.out.println(collection3.getClass() == collection2.getClass());//true
}
}
由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
package cn.liayun.generic;
import java.util.ArrayList;
public class GenericTest {
public static void main(String[] args) throws Exception {
ArrayList<Integer> collection3 = new ArrayList<Integer>();
// collection3.add("abc");//泛型是给编译器看的
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
System.out.println(collection3.get(0));
}
}