深刻理解Java泛型(二)通配符与嵌套


通配符与嵌套

上界通配符<? 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源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索