Java 泛型解惑之和

2019-06-15 00:00:00 java 解惑

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

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

class Fruit {}
class Apple extends Fruit {}

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

class Plate<T>{
    private 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());

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

error: incompatible types: Plate<Apple> cannot be converted to Plate<Fruit>

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

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

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

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

2 上界

下面就是上界通配符(Upper Bounds Wildcards)

Plate<? extends Fruit>

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

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

再扩展一下,食物分成水果和肉类,水果有苹果和香蕉,肉类有猪肉和牛肉,苹果还有两种青苹果和红苹果

//Lev 1
class Food{}

//Lev 2
class Fruit extends Food{}
class Meat extends Food{}

//Lev 3
class Apple extends Fruit{}
class Banana extends Fruit{}
class Pork extends Meat{}
class Beef extends Meat{}

//Lev 4
class RedApple extends Apple{}
class GreenApple extends Apple{}

在这个体系中,上界通配符Plate<? extends Fruit>覆盖下图中蓝色的区域
《Java 泛型解惑之<? extends T>和<? super T>》” /></p><h1><strong>3 下界</strong></h1><p>相对应的下界通配符(Lower Bounds Wildcards)</p><pre><code>Plate<? super Fruit>
</code></pre><p>表达的就是相反的概念:<strong>一个能放水果以及一切是水果基类的盘子</strong><br /> Plate<? super Fruit>是Plate的基类,但不是Plate的基类<br /> 对应刚才那个例子,Plate<? super Fruit>覆盖下图中红色的区域。<br /> <img layer-src=

相关文章