泛型简介
JAVA泛型是JDK 5中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
有许多原因促成了泛型的出现,而最引人注意的一个原因,就是为了创建容器类。
泛型方法
所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的
1 | public class Test { |
备注:类型参数也可以是多个,具有多个类型参数的泛型方法形如:public static <E, F> void printArray(E[] inputArray1, F[] inputArray2) {}
泛型类
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。举例如下:
1 | public class Box<T> { |
备注:泛型类也可以使用多个类型参数,形如:public class Box<T, E> {}
泛型接口
首先定义一个泛型接口Generator,如下:
1 | public interface Generator<T> { |
然后再定义一个生成器类来实现这个接口,如下:
1 | public class FruitGenerator implements Generator<String> { |
伪泛型 与 类型擦除
泛型的本质是参数化类型的应用,也就是说所操作的数据类型被指定为一个参数。泛型的概念在C++、C#以及JAVA中都有实现。但是实现的技术是不同的。
C#里面的泛型无论在程序源码中、编译后的IL中,或者是运行期的CLR中,都是切实存在的,List<int>
和List<String>
就是两个不同的类型,它们在系统运行时生成,有自己的虚方法表和类型数据,这种实现称为“类型膨胀”,在C#中基于这种方法实现的泛型称为“真实泛型”。JAVA语言中的泛型则不一样,它只在源码中存在,在编译后的字节码文件中就不存在了,而是在相应的地方插入了强制类型转化代码,因此,对于运行期的JAVA语言来说,ArrayList<int>
与ArrayList<String>
就是同一个类型,所以JAVA中泛型技术实际上是JAVA语言的一颗语法糖,JAVA语言中的泛型实现方法称为“类型擦除”,JAVA中基于这种方法实现的泛型称为“伪泛型”。
如下为使用JAVA泛型的一段源代码:
1 | public static void main(String[] args) { |
把上段JAVA代码编译成Class文件,然后再用字节码反编译工具进行反编译后,将会发现泛型都不见了,泛型类型都变回了原生类型,如下所示:
1 | public static void main(String[] args) { |
有两个函数,他们的参数类型不同,一个是List<String>
另一个是List<Integer>
,但是,这段代码是编译通不过的,是构成了不了重载Overload关系的。因为参数List<Integer>
和List<String>
编译之后都被擦除了,变成了一样的原生类型List
,擦除动作导致这两个方法的方法签名变得一模一样。
一点闲杂
不管是泛型类,泛型方法,还是泛型接口,在使用这些泛型时候,类型参数必须是抽象类型,不能是基本数据类型,如int/short,应该使用他们的封箱类Integer/Short等等。