泛型定义
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型(类型实参)。
假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?
答案是可以使用 Java 泛型。
使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
泛型原理:
泛型是java5才引入的,虚拟机其实并不支持泛型,为了向下兼容,Java在编译期间擦除了所有的泛型信息,这样Java就不需要产生新的类型到字节码,所有的泛型类型最终都是一种原始类型,在Java运行时根本就不存在泛型信息。
编译器如何擦除泛型:
- 检查泛型类型,获取目标类型
- 擦除类型变量,并替换为限定类型
- 如果泛型类型的类型变量没有限定<T>,则用Object作为原始类型
- 如果有限定<T extends Class1>,则用Class1作为原始类型
- 如果有多个限定类型
<T extends Class1 & Class2>
,则使用第一个边界Class1作为原始类
- 在必要时插入类型转换以保持类型安全
- 生成桥方法以在扩展时保持多态性
- 可以对比下类经过编译后的生成的字节码:
从下图1可以看见泛型都没了,add和get方法的T都被Object替代了,这就是编译器擦除了泛型
从下图2可看到如果有限定符,被擦除后则用限定类型替代。
从下图3看出编译后插入了类型转换
如下图4,图5,根据泛型擦除我们知道,接口CageInterface的get方法编译后其实是public Object get(){}
;而Cage的get方法被限定为public Animal get(){}
,所以编译器会实现一个桥方法,内部再去调用public Animal get(){}
方法,这样就保持了多态性。
限定通配符(为了更灵活的转型):
- <? extends Fruit>上界通配符(通常用于取数据):
- <? super Animal>下界通配符(通常用于接收数据):
这时候可以看一下Collections集合类的copy泛型方法:
public static <T> void copy(List<? super T> dest, List<? extends T> src)
可以看到src参数使用了上界通配符,dest参数使用了下界通配符,意味着src引用所指向的列表一定是T或者T的某个子类型,而dest引用范围是T或者它的父类,所以将src中的元素复制到dest一定是可以的,所以保证了从src复制到dest的类型正确性。
非限定通配符:
如List<?>,既不能读也不能写,等价于List<? extends Object>,对于List<?>编译器会进行安全检查,而List不会。