一、什么是泛型
泛型 ,它只在程序源码中存在,在编译后的字节码文件中,就已经被替换为原来的原生类型(Raw Type,也称为裸类型)了,并且在相应的地方插入了强制转型代码,因此对于运行期的Java语言来说,ArrayList< int>与ArrayList< String>就是同一个类。所以说泛型技术实际上是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型被称为伪泛型。
二、什么是类型擦除
类型擦除: 1.将所有的泛型参数T用其边界(extends的)类型替换。(例如T extends Comparable) 2.移除所有的类型参数T。
三、泛型的声明
泛型声明的位置
-
在类的声明上定义,例如RecyclerView 的 Adapter 类
public abstract static class Adapter< VH extends ViewHolder> {
-
在方法声明上定义,例如Collections 的 sort 方法
public static <T extends Comparable<? super T>> void sort(List< T> list) {
list.sort(null);
} -
比较特殊的一点,由于擦除操作,泛型参数会被边界替换,所以不支持声明的super操作
这样声明是合法的: class A< T extends Number>
这样声明是非法的:class A< T super Integer>
四、java 编译器处理泛型的过程
- JDK1.5版本之后class文件的属性表中添加了Signature和LocalVariableTypeTable等属性来支持泛型识别的问题
- 所以各种ide 查看 class 文件时,是进行了类型复原的,所以你会疑问(没有进行类型擦除啊!!!)
- 推荐使用jad 进行反编译 下载地址 https://varaneckas.com/jad/
public static void main(String args[]){
String s = max(" nihao");
List<String> list = new ArrayList<>();
maxTest4(list);
}
/***
* 方法定义了 泛型 参数
* @param a
* @param <T>
*/
public static <T> T max( T a ){
return a;
}
/***
* 方法定义了 泛型 ,并且 添加 了 上界
* @param a
* @param <T>
*/
public static <T extends Integer> T maxOne( T a){
return a;
}
/***
* 方法定义了泛型, 并且泛型的上界是泛型类 。此是 泛型类 的 泛型 可以设置为以下 三种情况
* 一 、 确切类型
* @param a
* @param <T>
* @return
*/
public static <T extends Comparable<Integer>> T maxTwo( T a){
return a;
}
/***
* 方法定义了泛型, 并且泛型的上界是泛型类 。此是 泛型类 的 泛型 可以设置为以下 三种情况
* 二、 方法定义的 泛型 T
* @param a
* @param <T>
* @return
*/
public static <T extends Comparable< T >> T maxTest( T a){
return a;
}
/***
* 方法定义了泛型, 并且泛型的上界是泛型类 。此是 泛型类 的 泛型 可以设置为以下 三种情况
* 三、 带通配符的泛型
* @param a
* @param <T>
* @return
*/
public static <T extends Comparable< ? super Integer >> T maxTest2( T a){
return a;
}
/***
* 函数没有定义泛形,函数形参的类型 是 泛型类
* @param comparable
* @return
*/
public static int maxTest3(Comparable<? super Integer> comparable){
return comparable.compareTo(20);
}
public static void maxTest4(List<?> list) {
}
以下是反编译 class 文件后的代码。 验证了上文所说的类型擦除: 1.将所有的泛型参数用其最左边界(最顶级的父类型)类型替换
public static void main(String args[])
{
String s = (String)max(" nihao");
List list = new ArrayList();
maxTest4(list);
}
public static Object max(Object a)
{
return a;
}
public static Integer maxOne(Integer a)
{
return a;
}
public static Comparable maxTwo(Comparable a)
{
return a;
}
public static Comparable maxTest(Comparable a)
{
return a;
}
public static Comparable maxTest2(Comparable a)
{
return a;
}
public static int maxTest3(Comparable comparable)
{
return comparable.compareTo(Integer.valueOf(20));
}
public static void maxTest4(List list1)
{
}
五、泛型与原始类型
无限制通配符 Set< ?> 与原始类型 Set 之间有什么区别? 问号真的给你放任何东西吗? 这不是要点,但通
配符类型是安全的,原始类型不是。 你可以将任何元素放入具有原始类型的集合中,轻易破坏集合的类型不变性。
你不能把任何元素(除 null 之外)放入一个 Collection< ?> 中。 而且无法预测你会get 到怎么的对象。
使用 完全通配符
以Collections 的源码为例 :强转为原生类型
public static void swap(List<?> list, int i, int j) {
// instead of using a raw type here, it's possible to capture
// the wildcard but it will require a call to a supplementary
// private method
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
或者 使用无泛型的Iterator
public static void reverse(List<?> list) {
int size = list.size();
if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {
for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
swap(list, i, j);
} else {
// instead of using a raw type here, it's possible to capture
// the wildcard but it will require a call to a supplementary
// private method
ListIterator fwd = list.listIterator();
ListIterator rev = list.listIterator(size);
for (int i=0, mid=list.size()>>1; i<mid; i++) {
Object tmp = fwd.next();
fwd.set(rev.previous());
rev.set(tmp);
}
}
}
六、限定通配符
限定通配符为 extends 和 super
- java 的编译器 不会认为 List< Integer> 是 List< Number> 的子类型
List<Number> list1 ;
List<Integer> list2 = new ArrayList<>();
list1 = list2; // 这行代码会编译报错
List<Number> list0 =null;
List<Integer> list1 =null;
List<? extends Number> list2 = null ;
list2 = list1;
list2 = list0;
使用 限制通配符 extends 可以解决这种问题 ,并且保证了类型安全。
也就是编译器 认为 List< Number> 是 List< ? extends Number> 的子类型
List< Integer> 是 List<? extends Number> 的子类型
- super 关键字
List<? super Integer> super1 = null;
List<Integer> super2 = null;
List<Number> super3 = null;
super1 = super2;
super1 = super3;
也就是编译器 认为 List< Integer> 是 List< ? super Integer> 的子类型
List< Number> 是 List<? super Integer> 的子类型
以 mySort 方法为例。
// 自己定义的 sort 方法 ,只做编译测试
public static <T extends Comparable< T>> void mySort(List<T> list) {
return;
}
public static class Food implements Comparable<Food>{
public int price;
public Food(int price) {
this.price = price;
}
@Override
public int compareTo(@NonNull Food o) {
return price - o.price;
}
}
public static class Milk extends Food{
public Milk(int price) {
super(price);
}
}
<T extends Comparable< T>> 表示:
类型 T 必须实现 Comparable 接口,并且这个接口的类型是 T。T 的实例之间才能相互比较大小。
由于是Milk 从父类那里继承实现的是Comparable< Food> ,以下代码编译会报错
List<Milk> milkList = new ArrayList<>();
mySort(milkList); // 编译报错
错误: 无法将类 GenericTest中的方法 mySort应用到给定类型;
需要: List<T>
找到: List<Milk>
原因: 推断类型不符合上限
推断: Milk
上限: Comparable<Milk>
其中, T是类型变量:
T扩展已在方法 <T>mySort(List<T>)中声明的Comparable<T>
将上面的函数改为 T extends Comparable< ? super T >
public static <T extends Comparable< ? super T>> void mySort(List<T> list) {
return;
}
执行
List<Milk> milkList = new ArrayList<>();
mySort(milkList); // 编译通过
编译器推断类型T 是 Milk 。 Milk 实现了 Comparable< Food> 接口
编译器 认为 Comparable< Food> 是 Comparable< ? super Milk> 的子类。
编译通过。
参考资料 Java的类型擦除