一、java泛型基本概念
泛型(Generic Type或Generics)是对Java语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看做是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的占位符一样,泛型的体现主要是在集合框架里面可以看到,JCF里面应该是1.5里面使用泛型最多的地方。Java语言引入泛型是一个较大的功能增强,不仅语言、类型系统和编译器有了大变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为了泛型化的了。
Java语言中的泛型基本上完全在编译器中实现,由编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码。这种实现技术称为擦除(erasure,编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)。
二、什么是类型擦除
类型擦除指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。
类型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
具体的转化过程大致分为以下几个部分:
- 将参数化类型中的类型参数
擦除(erasure)掉; - 将类型变量用
上限(upper bound)取代,通常情况下这些上限是Object。这里的类型变量是指实例域,本地方法域,方法参数以及方法返回值中用来标记类型信息的变量,例如:实例域中的变量声明A elem;,方法声明Node (A elem){};,其中,A用来标记elem的类型,它就是类型变量。 - 添加类型转换并插入
桥方法(bridge method),以便覆盖(overridden)可以正常的工作。
三、泛型优点
- 类型安全。泛型的一个主要目标就是提高Java程序的类型安全。使用泛型可以使编译器知道变量的类型限制,进而可以在更高程度上验证类型假设。如果没有泛型,那么类型的安全性主要由程序员来把握,这显然不如带有泛型的程序安全性高。
- 消除强制类型转换。泛型可以消除源代码中的许多强制类型转换,这样可以使代码更加可读,并减少出错的机会。
四、泛型的类型限制
JSR-14中规定:
- 不应在静态成员中引用封闭类型参数
- 不能用基本类型实例化泛型类型参数
- 不能在数据类型转换或instanceof操作中使用类型参数
- 不能在new操作符中使用类型参数
- 不能在类定义的implements或extends子句中使用类型参数
五、泛型和反射
既然泛型信息在编译后已完全抹去,那如何在运行时得到参数化的类型呢?
泛型是在Java平台上作为编译时转换实现的。编译器实际上生成与使用非泛型源代码时相同的字节指令,插入运行时类型转换以在每次访问时将值转换为正确的类型。尽管是相同的字节码,但是类型参数信息用一个新的签名(signature)属性记录在类模式中。JVM在装载类时记录这个签名信息,并在运行时通过反射使它可用。
六、通配符类型和受限泛型
通配符,使用一个?标识类型参数,是一种表示类或方法行为对于未知类型的类型约束的方法,比如“不管这个方法的参数x和y是哪种类型,它们必须是相同的类型”。
通配符类型List<?>、List<T>、原始List和具体List<Object>都不相同。如果说变量x具有List<?>类型,标识存在一些T类型,其中x是List<T>类型。
尽管我们不知道其元素的具体类型,这并不代表它具有任意内容,而是指我们并不了解内容的类型限制是什么--但我们知道存在某种限制。
另一方面,原始类型List是异构的,我们不能对其元素有任何类型限制,具体类型List<Object>表示我们明确地知道它能包含任何对象。
在非?通配符的使用过程里面,一般的通配符如T相当于是定义了一个类型,该类型是可以直接作为Java语言里面的类型来进行使用的,在定义这种类型的泛型类里面可以直接使用。
七、泛型不是协变(covariant)的,而数组则是协变的
数组是协变的,因为Integer是Number的子类型,数组类型Integer[]是Number[]的子类型,因此在任何需要Number[]值的地方都可以提供一个Integer[]值。
泛型不是协变的,List<Integer>不是List<Number>的子类型,试图在要求List<Number>的位置提供List<Integer>是一个类型错误。
因为数组和泛型的这个冲突,所以java语法规定不能创建参数化类型组成的数组,因为这样的数组会破坏类型的安全性。
java不允许这样的写法:List<Integer>[] list = new ArrayList<Integer>[]{};
但是List<Integer>[]本身可以作为类型参数,如:List<List<Integer>[]> list = new ArrayList<List<Integer>[]>();
另外未绑定的参数化类型数组new ArrayList<?>[3]是合法的,因为ArrayList<?>是可具化类型(reifiable type)。