java泛型可以被写在方法上,接口上,类上面以及参数定义上。
在为什么使用泛型中,oracle提到相比于没有泛型的代码,泛型提供了:
- 编译检查
- 消除强制转型
- 提高代码可读性
本文试图从容器角度来分析泛型,理解泛型的好处以及泛型的表现。我们将list,map之类的可以存放数据的对象成为容器。在没有泛型出现之前,容器的类型是不可知的,只有开发者知道容器内可能放什么东西,但是java对此并没有限制,也即是开发者可以将任意的对象添加进容器内,反之从容器中取出的的元素也可以是任意对象。下面的情况很容易发生。
List intList = new ArrayList();
intList.add("element");
Integer el = (Integer) intList.get(0);
将约束交给开发者的大脑,对于后续维护以及开发都是潜在的危险,因为后续可能无法正确使用对应的容器,更不用说取出元素的时候要添加强制转型来毁坏代码可读性。在容器中,泛型提醒人们容器在运行时装的是同一种类的对象。修改后的代码强壮性更高也更易读,包括约束也浮上纸面。
List<Integer> intList = new ArrayList();
intList.add(1);
Integer el = intList.get(0);
有super和extend的泛型
在容器中,泛型使容器可以成为特定的容器,可以放置指定的类,比如List和List可以分别放置Number对象和Integer对象。但是这两类容器是无法互相赋值的。尽管Integer继承于Number,在java编译器看来这是两个不同的容器,假如想泛指多种容器,则需要使用super或extend来规定下限或上限。
上限-extend
以Number为例,List<? extend Number>泛指了List,List,List等所有继承了Number类的子类构成的容器,包括Number在内,其中Number是上限,再往上的类以及其他的类不被泛指。因此从List<? extend Number>中取出的一定是个Number对象,可以使用Number类有的方法。List,List,List定义的对象可以赋值给List<? extend Number>定义的对象,但是无法反过来赋值。
下限-super
以Integer为例,List<? super Number>泛指了List,List,List等Integer继承树上的类构成的容器,此时Integer是下限,但是假如Integer有子类,也会包含在里面。同样,List,List,List定义的对象可以赋值给List<? super Number>定义的对象,但是无法反过来赋值。
oracle官网给了一个图帮助我们理解,底下的类可以赋值给箭头指向的类。

例子
定义以下的内部类,其中Box类是容器,Fruit类是Food类的子类
static class Box<T> {
private T t;
Box() {}
Box(T t) {
this.t = t;
}
T getT() {
return t;
}
void setT(T t) {
this.t = t;
}
}
static class Food {}
static class Fruit extends Food {}
定义三个变量
Object object = new Object();
Food food = new Food();
Fruit fruit = new Fruit();
以下的代码illegal注释代表编译报错:
Box<? extends Food> box1= new Box<>(food);
box1.setT(food); // illegal (case1)
box1.setT(fruit); // illegal (case2)
box1.setT(object); // illegal (case3)
food = box1.getT();
fruit = box1.getT(); // illegal (case4)
box1在运行时可能是一个food盒子,也可能是一个fruit盒子,这在编译期是无法确定的,因此所有的set操作(case1、2、3)都被禁止,理由是可能放置进去错误的类型。而get操作则因为extend的限制取出的一定是个food,但不一定是fruit(case4)
Box<? super Food> box2 = new Box<>(food);
box2.setT(food);
box2.setT(fruit);
box2.setT(object); // illegal (case5)
food = box2.getT(); // illegal (case6)
fruit = box2.getT(); // illegal (case7)
box2里面,错误的类型不被允许set进去(case5),但是下限类可以被set进去,上面的第二和第三行并没有报错,在super限制的继承树上,food和fruit肯定可以强转为继承树上的任意一个类。此处需要和box1做对比,case1中,food本身就是food,fruit也可以强转为food,为什么还被禁止set进去。因为food可以有多棵子树,假如存在子树meat,box1是放meat的,那么此时case2肯定是报错的,而同时,我们也无法得知case1里面的food是属于哪个类,其有可能是fruit强转的,对于泛型而言,不确定的类型肯定也会报错。
get操作因为无法super限制无法确切知道是哪个类而报错(case6,case7)
例外情况
由于需要兼容泛型出现前的情况,将一个不带泛型的容器赋值给一个带泛型的容器是被允许的,比如
List list = new ArrayList();
list.add("element");
List<Integer> intList = list;
参考 : oracle泛型