泛型定义
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替代了,这就是编译器擦除了泛型
图1.png
从下图2可看到如果有限定符,被擦除后则用限定类型替代。
图2.png
从下图3看出编译后插入了类型转换
图3.png
如下图4,图5,根据泛型擦除我们知道,接口CageInterface的get方法编译后其实是public Object get(){};而Cage的get方法被限定为public Animal get(){},所以编译器会实现一个桥方法,内部再去调用public Animal get(){}方法,这样就保持了多态性。
图4.png
图5.png
限定通配符(为了更灵活的转型):
- <? extends Fruit>上界通配符(通常用于取数据):
图6
如上图6 ,编译器报错,无法add,因为<? extends Animal>是限定animals这个引用类型范围是Animals或者其子类,只是代表这个范围,并不是具体某个引用类型,所以没法与任何类型匹配,add任何都报错(除了null),但是get是可以的,他一定是Animal的子类,只要用其父类接收就可以。
- <? super Animal>下界通配符(通常用于接收数据):
图7.png
如上图7,因为<? extends Animal>是限定animals引用类型范围是Animal或者它的父类(并非代表其指向的具体ArrayList对象存放的是Animal或者其父类),所以animals可以add任何Animal子类以及Animal,因为这些一定是这个引用类型的子类;也可以get,但是只能用Object接收,否则如上编译器报错,因此get没什么意义。
这时候可以看一下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不会。











网友评论