原博主[:]((25条消息) Java中 ? extends T 和 ? super T 的理解_一叶飘舟的博客-CSDN博客)
1、两者区别
<? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类,即?都是继承自T的。
<? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object, 即?都是T的父类
2、<? extend T>
上界<? extend T>不能往里存,只能往外取
public class Test {
public static void main(String[] args) {
List<? extends Car> list = new ArrayList<>();
list.add(new benz());
}
}
class Car{
}
class benz extends Car{
}
class audi extends Car{
}
class A7 extends audi{
}
编译器无法确定List所持有的类型,所以无法安全的向其中添加对象。可以添加null,因为null 可以表示任何类型。所以List 的add 方法不能添加任何有意义的元素,但是可以接受现有的子类型List 赋值。
List<? extends Car> list1 = new ArrayList<benz>();
list1.add(new benz());
报错的原因是编译器只知道容器内是Car或者它的派生类,但具体是什么类型不知道。可能是Benz可能是audi也可能是A7...编译器在看到后面用benz赋值以后,集合里并没有限定参数类型是“benz“。而是标上一个占位符:CAP#1,来表示捕获一个benz或benz的子类,具体是什么类不知道,代号CAP#1。然后无论是想往里插入什么编译器都不知道能不能和这个CAP#1匹配,所以就都不允许。
3、<? super T>
下界<? extend T>不影响往里存,但是往外取只能存在Object对象中
//super只能添加Father和Father的子类,不能添加Father的父类,读取出来的东西只能存放在Object类里
public class Test {
public static void main(String[] args) {
List<? super Car> list = new ArrayList<Car>();
list.add(new benz());
list.add(new audi());
list.add(new A7());
list.add(new Car());
benz benz = list.get(0); // error
Object obj = list.get(0);
}
}
class Car{
}
class benz extends Car{
}
class audi extends Car{
}
class A7 extends audi{
}
因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Car的基类,那往里存粒度比Car小的都可以。出于对类型安全的考虑,我们可以加入Car对象或者其任何子类(如benz)对象,但由于编译器并不知道List的内容究竟是Car的哪个超类,因此不允许加入特定的任何超类。而当我们读取的时候,编译器在不知道是什么类型的情况下只能返回Object对象,因为Object是任何Java类的最终祖先类。但这样的话,元素的类型信息就全部丢失了。
PECS原则
最后看一下什么是PECS(Producer Extends Consumer Super)原则,已经很好理解了:
- 频繁往外读取内容的,适合用上界Extends。
- 经常往里插入的,适合用下界Super。