目录
一、概述
二、和的使用及区别
(1)类型参数
(2)无界通配符
三、有界通配符、
四、泛型擦除
1、定义:Java泛型(generics
)泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
2、优势:Java语言引入泛型的优势在于安全、重用。 泛型在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。
3、作用:相比杂乱地使用Object声明
变量,再进行强制类型转换的代码而言,使用泛型机制编写的程序代码具有更好的安全性和可读性。泛型对于集合类尤其有用,例如,List
就是一个无处不在的泛型集合类。
List list=new ArrayList<>();
好奇List
和List>有什么区别呢?其实这个问题就是问无限定类型变量“ ” 和无界通配符“>”的区别。
讨论“
类型参数“
1、
List
最应该出现的地方,应该是定义一个泛型List容器。看看ArrayList的源码:
public class ArrayList extends AbstractList implements List, RandomAccess,
Cloneable, java.o.Serializable {
... ...
}
ArrayList
换一个简单的例子,我们自己定义一个新泛型容器叫Box
class Box{
private T item1;
private T item2;
}
类型参数能够对类型起到‘约束’的作用,例子中就是为了保证Box里的item1, item2都是同一个类型T。如,Box ,代表两个item都是String;Box 里两个item都是Integer。
*那什么时候会使用泛型类呢?
答:就是作为泛型类的成员字段或成员方法的参数间接出现。
class Box{
private List item;//泛型类成员字段
public List get(){return item;}//方法返回值
public void set(List t){item=t;}//方法参数
}
这里写成List
2、
Function类的reduce是个静态泛型方法,注意这方法是在普通类中定义的,而不是在泛型类中定义的。这里的List
出现在reduce方法参数\函数返回值\函数内部,也是为了保持泛型类型的一致性。
class Fuction{
public static List reduce(List list){
}
}
注意,类型变量public static
)的后面,返回类型的前面。
1、声明泛型类不能用>
首先,要明确通配符>不能拿来声明泛型。错误案例,如下:
class Box>{
//Error Example
private ? item1;
private ? item2;
}
通配符>作用:是使用定义好的泛型类。
例如,用>声明List容器的变量类型,然后用一个实例对象给它赋值的时候就比较灵活。
List> list = new ArrayList();
2、 >的CAP#1问题
List>这个写法非常坑。因为,通配符>会捕获具体的String类型,但编译器不叫它String,而是起个临时的代号,比如”CAP#1“。因此,list不能存String,任何元素都不行,只能存null。
List> list = new ArrayList();
list.add("hello"); //ERROR
//argument mismatch; String cannot be converted to CAP#1
list.add(111); //ERROR
//argument mismatch; int cannot be converted to CAP#1
另外,如果拿List>做参数,也会有奇妙的事情发生。还是刚才Box
class Box{
private List item;
public List get(){return item;}
public void set(List t){item=t;}
//把item取出来,再放回去, ERROR
public void getSet(Box> box){box.set(box.get());
//error: incompatible types: Object cannot be converted to CAP#1
}
}
新的getSet()方法,只是把item先用get()方法读出来,然后再用set()方法存回去。按理说不可能有问题。实际运行却会报错。原因和前面一样,用通配符>声明的类型泛型类Box,调用box.set()的参数类型被编译器捕获,命名为'CAP#1',和box>.get()返回的Object对象无法匹配。
3、解决方法
通过写一个helper()辅助函数。
class Box{
private List item;
public List get(){return item;}
public void set(List t){item=t;}
//getSet()方法存取元素
public void getSet(Box> box){helper(box);}
//helper()辅助函数
public void helper(Box box){box.set(box.get());}
}
常用的是 extends X>或者 super XXX>两种,带有上下界的通配符。
其中,上界可以有多层 extends X extends XX>。
注意:
- 对于无界通配符> extends 的通配符限定泛型,我们无法向里面添加元素(只可以添加null),只能读取其中的元素。
- 对于无界通配符> super 的通配符限定泛型,我们可以读取其中的元素,但读取出来的元素会变为 Object 类型。
Java的泛型基本上都是在编译器这个层次上实现的,在运行期生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。
我们先看一个例子:
ArrayList list1 = new ArrayList();
list1.add("abc");
ArrayList list2 = new ArrayList();
list2.add(123);
System.out.println(list1.getClass() == list2.getClass());
打印结果:true
结果显示list1和list2居然是同一个类型的ArrayList,在运行时我们传入的类型变量String和Integer都被擦除掉了,只剩下原始类型。原因是Java语言泛型在设计的时候为了兼容原来的旧代码,虚拟机并不知道泛型的存在,因为泛型在编译阶段就已经被处理成普通的类和方法了。类型擦除规则:
若泛型类型没有指定具体类型,用Object作为原始类型;
若有限定类型< T exnteds XClass >,使用XClass作为原始类型;
若有多个限定< T exnteds XClass1 & XClass2 >,使用第一个边界类型XClass1作为原始类型;