
通配符与嵌套
上界通配符<? extends T>
咱们先来看一个例子:web
class Fruit {}
class Apple extends Fruit {}
如今咱们定义一个盘子类:微信
class Plate<T>{
T item;
public Plate(T t){
item=t;
}
public void set(T t) {
item=t;
}
public T get() {
return item;
}
}
下面,咱们定义一个水果盘子,理论上水果盘子里,固然能够存在苹果编辑器
Plate<Fruit> p=new Plate<Apple>(new Apple());
你会发现这段代码没法进行编译。装苹果的盘子”没法转换成“装水果的盘子:ide
cannot convert from Plate<Apple> to Plate<Fruit>
从上面代码咱们知道,就算容器中的类型之间存在继承关系,可是Plate和Plate两个容器之间是不存在继承关系的。在这种状况下,Java就设计成Plate<? extend Fruit>来让两个容器之间存在继承关系。咱们上面的代码就能够进行赋值了ui
Plate<? extends Fruit> p=new Plate<Apple>(new Apple());
Plate<? extend Fruit>是Plate< Fruit >和Plate< Apple >的基类。咱们经过一个更加详细的例子来看一下上界的界限:spa
class Food{}
class Fruit extends Food {}
class Meat extends Food {}
class Apple extends Fruit {}
class Banana extends Fruit {}
class Pork extends Meat{}
class Beef extends Meat{}
class RedApple extends Apple {}
class GreenApple extends Apple {}
在上面这个类层次中,Plate<? extend Fruit>,覆盖下面的蓝色部分:.net
若是咱们往盘子里面添加数据,例如:设计
p.set(new Fruit());
p.set(new Apple());
你会发现没法往里面设置数据,按道理说咱们将泛型类型设置为? extend Fruit。按理说咱们往里面添加Fruit的子类应该是能够的。可是Java编译器不容许这样操做。<? extends Fruit>会使往盘子里放东西的set()方法失效。但取东西get()方法还有效code
缘由是:Java编译期只知道容器里面存放的是Fruit和它的派生类,具体是什么类型不知道,多是Fruit?多是Apple?也多是Banana,RedApple,GreenApple?编译器在后面看到Plate< Apple >赋值之后,盘子里面没有标记为“苹果”。只是标记了一个占位符“CAP#1”,来表示捕获一个Fruit或者Fruit的派生类,具体是什么类型不知道。全部调用代码不管往容器里面插入Apple或者Meat或者Fruit编译器都不知道能不能和这个“CAP#1”匹配,因此这些操做都不容许。orm
最新理解:一个Plate的引用,指向的多是一个Plate类型的盘子,要往这个盘子里放Banana固然是不被容许的。个人一个理解是:Plate表明某个只能放某种类型水果的盘子,而不是什么水果都能往里放的盘子
可是上界通配符是容许读取操做的。例如代码:
Fruit fruit=p.get();
Object object=p.get();
这个咱们很好理解,因为上界通配符设定容器中只能存放Fruit及其派生类,那么获取出来的咱们均可以隐式的转为其基类(或者Object基类)。因此上界描述符Extends适合频繁读取的场景。
下界通配符<? super T>
下界通配符的意思是容器中只能存放T及其T的基类类型的数据。咱们仍是以上面类层次的来看,<? super Fruit>覆盖下面的红色部分:
下界通配符<? super T>不影响往里面存储,可是读取出来的数据只能是Object类型。缘由是:下界通配符规定了元素最小的粒度,必须是T及其基类,那么我往里面存储T及其派生类都是能够的,由于它均可以隐式的转化为T类型。可是往外读就很差控制了,里面存储的都是T及其基类,没法转型为任何一种类型,只有Object基类才能装下。
PECS原则
最后简单介绍下Effective Java这本书里面介绍的PECS原则。
上界不能往里存,只能往外取,适合频繁往外面读取内容的场景。下界不影响往里存,但往外取只能放在Object对象里,适合常常往里面插入数据的场景。
<?>无限通配符
无界通配符 意味着能够使用任何对象,所以使用它相似于使用原生类型。但它是有做用的,原生类型能够持有任何类型,而无界通配符修饰的容器持有的是某种具体的类型。举个例子,在List<\?>类型的引用中,不能向其中添加Object, 而List类型的引用就能够添加Object类型的变量。
最后提醒一下的就是,List<\object>与List并不等同,List<\object>是List的子类。还有不能往List list里添加任意对象,除了null。
夯实基础,关注前沿,娱乐生活
掌握更多前沿技术,获取更多笑点
请关注--------喘口仙氣
本文分享自微信公众号 - 喘口仙氣(gh_db8538619cdd)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。