专栏首页Android开发实战深入理解Java泛型(二)通配符与嵌套

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

通配符与嵌套

上界通配符<? extends T>

我们先来看一个例子:

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());

你会发现这段代码无法进行编译。装苹果的盘子”无法转换成“装水果的盘子:

cannot convert from Plate<Apple> to Plate<Fruit>

从上面代码我们知道,就算容器中的类型之间存在继承关系,但是Plate和Plate两个容器之间是不存在继承关系的。在这种情况下,Java就设计成Plate<? extend Fruit>来让两个容器之间存在继承关系。我们上面的代码就可以进行赋值了

Plate<? extends Fruit> p=new Plate<Apple>(new Apple());

Plate<? extend Fruit>是Plate< Fruit >和Plate< Apple >的基类。我们通过一个更加详细的例子来看一下上界的界限:

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>,覆盖下面的蓝色部分:

如果我们往盘子里面添加数据,例如:

p.set(new Fruit());
p.set(new Apple());

你会发现无法往里面设置数据,按道理说我们将泛型类型设置为? extend Fruit。按理说我们往里面添加Fruit的子类应该是可以的。但是Java编译器不允许这样操作。<? extends Fruit>会使往盘子里放东西的set()方法失效。但取东西get()方法还有效

原因是:Java编译期只知道容器里面存放的是Fruit和它的派生类,具体是什么类型不知道,可能是Fruit?可能是Apple?也可能是Banana,RedApple,GreenApple?编译器在后面看到Plate< Apple >赋值以后,盘子里面没有标记为“苹果”。只是标记了一个占位符“CAP#1”,来表示捕获一个Fruit或者Fruit的派生类,具体是什么类型不知道。所有调用代码无论往容器里面插入Apple或者Meat或者Fruit编译器都不知道能不能和这个“CAP#1”匹配,所以这些操作都不允许。

最新理解:一个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),作者:Anymarvel

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 异步线程大师Handler(源码+图+demo+常见问题)

    Handler 机制 源码+图+常见问题+Demo 详细记录(本文内容略长,但内容较为详细,推荐Android开发者可深入观看.如有问题,欢迎指正)

    Anymarvel
  • Java注解深入浅出(一)-注解的真实面貌

    这处图片引自老罗的博客。为了避免不必要的麻烦,首先声明我个人比较尊敬老罗的。至于为什么放这张图,自然是为本篇博文服务,接下来我自会说明。好了,可以开始今天的博文...

    Anymarvel
  • java中equals,hashcode和==的区别

    byte,short,char,int,long,float,double,boolean 他们之间的比较,应用双等号(==),比较的是他们的值。

    Anymarvel
  • Java 泛型解惑之<? extends T>和<? super T>

    使用泛型的过程中,经常出现一种很别扭的情况 比如我们有Fruit类,和它的派生类Apple

    JavaEdge
  • 超50万行代码、GitHub 4200星:腾讯重磅发布全栈机器学习平台Angel 3.0

    【导读】腾讯首个AI开源项目Angel,正式发布一个里程碑式的版本:Angel 3.0。这是一个全栈机器学习平台,功能特性涵盖了机器学习的各个阶段,超过50万...

    腾讯大数据
  • 腾讯重磅发布全栈机器学习平台Angel 3.0

    ? 腾讯首个AI开源项目Angel,正式发布一个里程碑式的版本:Angel 3.0。这是一个全栈的机器学习平台,功能特性涵盖了机器学习的各个阶段,超过50万行...

    腾讯技术工程官方号
  • 腾讯Angel升级:加入图算法,支持十亿节点、千亿边规模!中国首个毕业于Linux AI基金会的开源项目

    【导语】Angel 是腾讯的首个AI开源项目,于 2016 年底推出、2017年开源。近日,快速发展的 Angel 完成了从 2.0 版本到 3.0 版本的跨...

    AI科技大本营
  • 腾讯开源全栈机器学习平台 Angel 3.0,支持三大类型图计算算法

    10 月 18 日,2019 中关村论坛平行论坛 ——AI WORLD 2019 世界人工智能峰会在北京启幕。新智元杨静、科大讯飞胡郁、微软王永东、华为王成录、...

    新智元
  • 编写模块化CSS——BEM

    用户1687375
  • C语言荣获2019年度编程语言

    这次C语言年增长2.4%,第二名是C#(+2.1%)、Python(+1.4%)和Swift(+0.6%)。为什么C语言仍然很流行?这一趋势背后的主要驱动力是...

    Qt君

扫码关注云+社区

领取腾讯云代金券