引入
再说这个之前,先来看一段代码:
假设有这么几个类及其继承关系,后面的例子也用这几个类作为基础示范
class People {
//人
}
class Man extends People {
//男人
}
class Woman extends People {
//女人
}
class Boy extends Man {
//男孩
}
可以看到,man是people的子类,那么:
List list= new ArrayList();
是否可以编译通过?
很明显,编译的时候报错了。man是people的子类,ArrayList是List的子类,但并不代表List
是List的子类。所以便有了
有限通配符
extends E>
extends E> 是 Upper Bound(上限) 的通配符,用来限制元素的类型的上限,比如:
List extends People> list_1 = null;
表示集合中的元素上限是People,即只能是People或People的子类,所以下面的赋值是合法的,编译时候不会报错:
但不能是其父类,否则编译时候就报错了:
接下来对其读写数据进行了解:
1、读
不管给该list_1如何赋值,可以保证的是里面存放的一定是People或People的子类,编译器可以确定获取的是People类型,所以可以直接从该集合中读取到People类型。即读取是允许的。
2、写
People、Man、Woman都是People类或其子类,但是这里却是编译错误的。原因是? extends People>仅仅告诉编译器该集合元素类型上限是People,这里编译器并不能确定具体的类型,即它可能实际指向了Man,但是你却add一个Woman类型,所以这里编译器不允许这么做。
super E>
super E> 是 Lower Bound(下限) 的通配符 ,用来限制元素的类型下限,比如:
List super Man> list_4 = null;
该表示给出了集合中元素的下限是Man,即只能为Man或者Man的父类,而不能是Man的子类,如下:
接下来对其读写数据进行了解:
1、读
允许从该集合获取元素,但是无法保证里面存放的是Man或者是Woman,唯一可以确定的是存放的是Object或其子类,而无法确定具体的类型。
这样都没错,但是实际用的时候还是要注意,像这样获取Woman可能导致异常。
2、写
可以确定的是集合中的元素一定是Man或Man的子类,所以添加Man或Boy都是正确的,但是不能添加非Man的子类:
使用场景
很多时候都是用它来当作方法中的形参。
这里先了解下PECS法则
1、PECS
PECS指“Producer Extends,Consumer Super”。换句话说,如果参数化类型表示一个生产者,就使用 extends T>;如果它表示一个消费者,就使用 super T>
2、例子
这里使用网上常见的例子水果来说明,有如下关系:
假设此时有个水果供应商Produce,
class Produce {
public void produce(List list) {
for (E e : list) {
//生产...
System.out.println("批量生产...");
}
}
}
它主要销售水果
Producer p = new Produce<>();
List pears = new ArrayList();
p.produce(pears);
这样并没有什么问题。但是万一他突然想换成销售苹果了,此时:
这样就会发现,编译并不能通过,因为List
已经在初始化时确定为Pear了,而不再兼容Appler类型,即使你最开始使用的是Produce,即方法produce的参数list为List,虽然Apple和Pear是Fruit的子类,但是由上面的引入知识知道,List并不是List的父类,即这样也是行不通的,所以这里就需要使用List extends E> list了。
修改后如下:
class Producer {
public void produce(List extends E> list) {
for (E e : list) { //利用 extends E>读取的特性
//生产...
}
System.out.println("批量生产完成...");
}
}
此时只要供应商new的时候为Fruit,则生产的货物只要为Fruit或其子类即可,所以Pear和Apple都可通过。如下:
接着举一个消费者的例子(可能例子举得不是很好)
//消费者
class Consumer {
public E consume(List list) {
E e = list.get(0); //模拟消费一个(感觉用队列比较合适)
return e;
}
}
每次消费者都从一个list中消费一个。加入有一个红苹果消费者:
这里是没什么问题的,但是红苹果也是苹果,如果这样呢:
这时候, super E>派上用场了。
//消费者
class Consumer {
public E consume(List super E> list) {
E e = (E) list.get(0); //模拟消费一个(感觉用队列比较合适)
return e;
}
}
此时再按刚才的操作:
编译并不会出问题了。
其实,在java提供的许多类库里就有用到了,比如Collections的静态方法copy:
为了保证在list复制过程中类型的安全,限制了原list的上限,保证了目标数组的下限。
参考链接: https://www.cnblogs.com/wangmingshun/p/5389341.html
http://blog.csdn.net/asdfsadfasdfsa/article/details/52794573