泛型是JDK 1.5的一项新特性,它的本质是参数化类型(Parameterized Type)的应用,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
泛型思想早在C++语言的模板(Templates)中就开始生根发芽,在Java语言处于还没有出现泛型的版本时,只能通过Object是所有类型的 父类和类型强制转换两个特点的配合来实现类型泛化。例如在哈希表的存取中,JDK 1.5之前使用HashMap的get()方法,返回值就是一个Object对象,由于Java语言里面所有的类型都继承于 java.lang.Object,那Object转型成任何对象都是有可能的。但是也因为有无限的可能性,就只有程序员和运行期的虚拟机才知道这个 Object到底是个什么类型的对象。在编译期间,编译器无法检查这个Object的强制转型是否成功,如果仅仅依赖程序员去保障这项操作的正确性,许多 ClassCastException的风险就会被转嫁到程序运行期之中。 泛型技术在C#和Java之中的使用方式看似相同,但实现上却有 着根本性的分歧, C#里面泛型无论在程序源码中、编译后的IL中(Intermediate Language,中间语言,这时候泛型是一个占位符)或是运行期的CLR中都是切实存在的,List<int>与 List<String>就是两个不同的类型,它们在系统运行期生成,有自己的虚方法表和类型数据,这种实现称为类型膨胀,基于这种方法实现 的泛型被称为真实泛型。 Java语言中的泛型则不一样,它只在程序源码中存在,在编译后的字节码文件中,就已经被替换为原来的原生类型 (Raw Type,也称为裸类型,也即没有了后面的参数)了,并且在相应的地方插入了强制转型代码,因此对于运行期的Java语言来说,ArrayList<int>与 ArrayList<String>就是同一个类。所以说泛型技术实际上是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型 擦除,基于这种方法实现的泛型被称为伪泛型。 也就是说Java中的泛型是个假泛型,仅仅只是在编译器那边做了语法检查而已,和C#里的泛型不一样的。
基本上,不管你在List<>里面写什么类型,编译通过了以后运行时全部都是Object。
代码清单10-2是一段简单的Java泛型例子,我们可以看一下它编译后的结果是怎样的? 代码清单 10-2 泛型擦除前的例子 Java代码- public static void main(String[] args) {
- Map<String, String> map = new HashMap<String, String>();
- map.put("hello", "你好");
- map.put("how are you?", "吃了没?");
- System.out.println(map.get("hello"));
- System.out.println(map.get("how are you?"));
- }
- public static void main(String[] args) {
- Map map = new HashMap();
- map.put("hello", "你好");
- map.put("how are you?", "吃了没?");
- System.out.println((String) map.get("hello"));
- System.out.println((String) map.get("how are you?"));
- }
- public class GenericTypes {
- public static void method(List<String> list) {
- System.out.println("invoke method(List<String> list)");
- }
- public static void method(List<Integer> list) {
- System.out.println("invoke method(List<Integer> list)");
- }
- }
- public class GenericTypes {
- public static String method(List<String> list) {
- System.out.println("invoke method(List<String> list)");
- return "";
- }
- public static int method(List<Integer> list) {
- System.out.println("invoke method(List<Integer> list)");
- return 1;
- }
- public static void main(String[] args) {
- method(new ArrayList<String>());
- method(new ArrayList<Integer>());
- }
- }
- invoke method(List<String> list)
- invoke method(List<Integer> list)
public void inspect(List这段代码中,inspect方法接受List<Object>作为参数,当在test方法中试图传入List<String>的 时候,会出现编译错误。假设这样的做法是允许的,那么在inspect方法就可以通过list.add(1)来向集合中添加一个数字。这样在test方法 看来,其声明为List<String>的集合中却被添加了一个Integer类型的对象。这显然是违反类型安全的原则的,在某个时候肯定会 抛出。因此,编译器禁止这样的行为。编译器会尽可能的检查可能存在的类型安全问题。对于确定是违反相关原则的地方,会给出编译错误。当编译器无法判断类型的使用是否正确的时候,会给出警告信息。