简述
Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
下面我们通过这几个问题来理解Java泛型。
- 泛型的作用是什么?
- 为什么会有泛型?
- 什么是泛型擦除?
- 什么是通配符?
- 什么是上边界?什么是下边界?
1 泛型的作用
第一个问题,泛型的作用是什么?
简单概括就是,使得类型可以像方法的参数那样传递。
2 为什么会有泛型?
那么为什么会有泛型出现了,这也就要看到泛型出现到底解决了啥问题。
举个例子,现在有一个水缸,然后我们往里面放条红鲤鱼。
// 红鲤鱼对象
public class GreenCarp {
public void move() {
System.out.println("绿鲤鱼在吐泡泡!");
}
}
// 水缸对象
public class Tank {
private RedCarp redCarp;
public RedCarp getRedCarp() {
return redCarp;
}
public void setRedCarp(RedCarp redCarp) {
this.redCarp = redCarp;
}
}
// 放鱼
public class Test {
public static void main(String[] args) {
Tank tank = new Tank();
tank.setRedCarp(new RedCarp());
tank.getRedCarp().move();
}
}
然后我们又想把水缸里换成绿鲤鱼,这个时候,有人可能会直接定义一个Object在水缸对象里。
public class Tank {
private Object obj;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
public class Test {
public static void main(String[] args) {
Tank tank = new Tank();
tank.setObj(new RedCarp());
((RedCarp) tank.getObj()).move();
tank.setObj(new GreenCarp());
((GreenCarp) tank.getObj()).move();
}
}
看起来没啥问题,但Object用多了,对于程序员来说也会成为非常麻烦的事情。比如,他有可能没转换为正确的类型,但编译器并不会爆红!运行后却出现错误!
public class Test {
public static void main(String[] args) {
Tank tank = new Tank();
tank.setObj(new RedCarp());
((RedCarp) tank.getObj()).move();
tank.setObj(new GreenCarp());
((RedCarp) tank.getObj()).move(); // 写成了RedCarp
}
}
public class Tank {
private T t;
public T getObj() {
return t;
}
public void setObj(T t) {
this.t = t;
}
}
3 泛型擦除
讲泛型擦除前,我们可以看个例子,
public class Test {
public static void main(String[] args) {
List l1 = new ArrayList();
List l2 = new ArrayList();
System.out.println(l1.getClass() == l2.getClass());
}
}
各位觉得上面的代码会输出什么?true or false?
答案是true,为什么呢?为什么l1与l2的两个运行时类是相等的。
这就是泛型擦除导致的。
虽然两个对象在编码时候看上去差别很大,但是在运行时已经被反射擦除泛型了。
示例:
List l1 = new ArrayList();
l1.add("8848");
Class c = l1.getClass();
Method m = c.getMethod("add", Object.class);
m.invoke(l1, 8848);
System.out.println(l1);
小结:
泛型擦除是为了兼容jdk老版本的编码。而泛型只存在于编译时期,泛型使编译器可以在编译期间对类型进行检查以提高类型安全,减少运行时由于对象类型不匹配引发的异常。
4 通配符
通配符 ?
Java的继承关系,在泛型中如果不做任何声明修饰,是不被认可的。所以需要泛型处理。
5 上边界与下边界
上边界
? extends T
代表可以传入T或T的子类类型。
下边界
? super T
代表传入必须是T或T的父类类型。
什么时候用上边界?
上边界不能放,因为上边界作用下,不知道要放什么样的类型 所以会报错,但是可以取。
什么时候用下边界?