Java——聊聊<? extends T>和<? super T>的含义及区别

文章目录:

1.简介

2.上界通配符

3.下界通配符

4.简单小总结


1.简介

<? extends T>    <? super T> 是Java泛型中的“通配符(Wildcards)”“边界(Bounds)”的概念。

  • <? extends T>:是指 “上界通配符(Upper Bounds Wildcards)”
  • <? super T>:是指 “下界通配符(Lower Bounds Wildcards)”

为什么要用通配符和边界?

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

class Fruit {}
class Apple extends Fruit {}

然后有一个最简单的容器:Plate类。盘子里可以放一个泛型的“东西”。我们可以对这个东西做最简单的“放”和“取”的动作:set( )和get( )方法。

class Plate<T> {
    private T item;

    public Plate(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}

现在我定义一个“水果盘子”,逻辑上水果盘子当然可以装苹果。

Fruit fruit = new Apple(); //正确
Plate<Fruit> p = new Plate<Apple>(new Apple()); //报错了

但实际上Java编译器不允许这个操作。会报错,“装苹果的盘子”无法转换成“装水果的盘子”。

实际上,编译器脑袋里认定的逻辑是这样的:

  • 苹果 IS-A 水果
  • 装苹果的盘子 NOT-IS-A 装水果的盘子

所以,就算容器里装的东西之间有继承关系,但容器之间是没有继承关系的。所以我们不可以把Plate的引用传递给Plate。

为了让泛型用起来更舒服,Sun公司的那些大牛们就想出了 <? extends T> 和 <? super T> 的办法,来让”水果盘子“和”苹果盘子“之间发生关系。


2.<? extends T>上界通配符

下面代码就是“上界通配符(Upper Bounds Wildcards)”:

Plate<? extends Fruit>

翻译成人话就是:一个能放水果以及一切是水果派生类的盘子。再直白点就是:啥水果都能放的盘子。这和我们人类的逻辑就比较接近了。Plate<? extends Fruit>Plate<Apple>最大的区别就是:Plate<? extends Fruit>Plate<Fruit>以及Plate<Apple>的基类。直接的好处就是,我们可以用“苹果盘子”给“水果盘子”赋值了。

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

假设,我有下面这样的继承结构:↓↓↓ 

class Food {}

class Fruit extends Food {}
class Meat extends Food {}

class Apple extends Fruit {}
class Banana extends Fruit {}
class Beef extends Meat {}
class Pork extends Meat {}

class RedApple extends Apple {}
class GreenApple extends Apple {}

此时,上面的 <? extends Fruit> 就表示下图的红色部分。

class MainTest1 {
    public static void main(String[] args) {
        Plate<? extends Fruit> plate = new Plate<>(new Apple());
        //不能存入任何元素
//        plate.setItem(new Fruit());
//        plate.setItem(new Apple());

        //读取出来的东西只能存放在Fruit或它的基类里
        Fruit fruit = plate.getItem();
        Food food = plate.getItem();
        Object obj = plate.getItem();
        //Apple apple = plate.getItem(); //报错
    }
}

但是呢,在上界通配符中存在着一定的缺陷:不能往里存,只能往外取。

<? extends Fruit>会使往盘子里放东西的set()方法失效。但取东西get( )方法还有效。比如下面例子里两个set()方法,插入Apple和Fruit都报错。

  • setItem() : 编译器只知道元素类型是 Fruit 或 Fruit 的子类,所以有可能是 Fruit、Apple、Banana、RedApple、GreenApple中的某一个类型,那么当我们向其中添加元素时,编译器并不知道具体是哪一个 (派生) 类。所以这种操作是不允许的。
  • getItem() : 即使编译器不知道此时类、集合中的元素是 Fruit、Apple、Banana、RedApple、GreenApple 中的哪一个,但是能够确定的是这些元素都是由 Fruit 派生出来的,所以上面的代码中 getItem 方法是可以用 Fruit、Fruit的父类Food、总类Object 接收。

3.<? super T>下界通配符

下面代码就是“下界通配符(Upper Bounds Wildcards)”:

Plate<? super Fruit>

表达的就是相反的概念:一个能放Fruit以及一切是Fruit基类的盘子。Plate<? super Fruit>Plate<Fruit>的基类,但不是Plate<Apple>的基类。对应刚才那个例子,Plate<? super Fruit>覆盖下图中红色的区域。 

Plate<? super Fruit> plate = new Plate<>(new Fruit());

假设,我有下面这样的继承结构:↓↓↓ 

class Food {}

class Fruit extends Food {}
class Meat extends Food {}

class Apple extends Fruit {}
class Banana extends Fruit {}
class Beef extends Meat {}
class Pork extends Meat {}

class RedApple extends Apple {}
class GreenApple extends Apple {}

此时,上面的 <? superFruit> 就表示下图的蓝色部分。

class MainTest2 {
    public static void main(String[] args) {
        Plate<? super Fruit> plate = new Plate<>(new Fruit());
        //存入元素正常
        plate.setItem(new Fruit());
        plate.setItem(new Apple());
        //读取出来的东西只能存放在Object类中
        //Apple apple = plate.getItem();
        //Fruit fruit = plate.getItem();
        Object obj = plate.getItem();
    }
}

但是呢,在下界通配符中存在着一定的缺陷:下界<? super T>可以往里存,但往外取只能放在Object对象里。

使用下界<? super Fruit>会使从盘子里取东西的get()方法部分失效,只能存放到Object对象里。set( )方法正常。

  • setItem() : 编译器只知道元素类型是 Fruit 或者 Fruit 的基类或父类,所以有可能是 Fruit、Food、Object 其中的一个类型。编译器知道类型的下界是 Fruit ,根据类型向上兼容的特性,所以可以添加的元素是 Fruit 以及 Fruit 的派生类。
  • getItem() : 既然编译器不确定集合类型是 Fruit 或者 Fruit 的基类或父类中的哪一种,可能是Fruit,可能是Food,那么返回类型只能是它们的共同父类 Object。但是这里可以通过强制类型转换成相应的Fruit的子类。

4.简单小总结

最后看一下什么是PECS(Producer Extends Consumer Super)原则,已经很好理解了:

  • <? extends C> 适合大量做获取操作的情景。
  • <? super C> 适合大量做添加操作的情景。

特点:<? extends C> 的 add() 被限制,<? super C> 的 get() 被限制。

  • 17
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胜天半子-祁同伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值