public class Food {}
class Fruit extends Food {}
class Apple extends Fruit {}
class Banana extends Fruit{}
class GenericTest {
public void testExtends(List<? extends Fruit> list){
//报错,extends 为上界通配符,只能取值,不能放.
//因为 Fruit 的子类不只有 Apple 还有 Banana,这里不能确定具体的泛型到底是 Apple 还是Banana,所以放入任何一种类型都会报错
list.add(new Apple()); // 报错
//可以正常获取
Fruit fruit = list.get(1);
}
public void testSuper(List<? super Fruit> list){
//super 为下界通配符,可以存放元素,但是也只能存放当前类或者子类的实例,以当前的例子来讲,
//无法确定 Fruit 的父类是否只有 Food 一个(Object 是超级父类)
}
}
具体解释一下:
对于定义的 List<? extends Fruit> list,给他初始化的时候,指定的类型必须是Fruit或Fruit的子类,即
List<? extends Fruit> list = new ArrayList<Apple>(); // compile success
List<? extends Fruit> list2 = new ArrayList<Food>(); // compile fail
所以往里面添加的时候你没法知道具体是哪个子类,比如初始化为new ArrayList(),然后添加是Banana 类型,这样就导致了类型错误。因此list在这里是不能添加元素的,只能获取,因为可以知道里面的元素一定是Fruit及其子类型的一种,因此可以用Fruit去接收。
那肯定有人会有这样的疑问?那不能添加岂不是就一个空列表,还有什么意义呢。这里就要说到使用场景了,一个都是用在方法的形参里面定义,在传参的时候就传的是一个有值的列表。当然如果不是在形参中你也要这么用,当然也是可以的,如下:
List<? extends Fruit> list;
List<Apple> list2 = new ArrayList<Apple>();
list2.add(new Apple());
list2.add(new Apple());
list2.add(new Apple());
list = list2;
list.size();
对于List<? super Fruit> list就是同样的道理了,初始化的类型必须是Fruit及其父类,因此可以放心的往里面添加Fruit及其子类,但是从列表里面取得时候就只能用Object类去接收,从定义中无法知道具体是Fruit或它的哪个父类。