<? extends T><? super T> 的使用

先定义三个类

public class Tea {}

public class GreenTea extends Tea {}

public class BlackTea extends Tea {}

再定义一个容器类

@Data
@AllArgsConstructor
public class Cup<T> {
    private T t;
}

定义一个茶杯让它去装绿茶

Cup<Tea> teaCup = new Cup<GreenTea>(new GreenTea());

这个操作 Java 会报错

Incompatible types.
Required: Cap<Tea>
Found:    Cap<GreenTea>

那么问题来了,按照我们的逻辑,茶杯去装绿茶是完全可以的,但编译器认为茶杯只能装“茶”,

编译器的世界里:

所以上下界通配符的出现就能让“装绿茶的杯子”和“装茶的杯子”关联起来

那么现在定义一个上界

Cup<? extends Tea> teaCup

这个 teaCup 能盛茶和所有茶的子类,说直白点就是能盛所有的茶,所以现在这个杯子里是可以装绿茶的

Cup<? extends Tea> teaCup = new Cup<>(new GreenTea());

那么同时出现了另一个问题,单看 teaCup 这个变量,它的类型是 Cup<? extends Tea>,我们并不知道这个里面放的具体是 Tea、GeeenTea 还是 BlackTea,那么我们在调用 setT() 方法时无论传什么实例进去都会报错(假设定义时的 T 是 GreenTea,setT() 传入的是 BlackTea 必定报错)

//都会报错
teaCup.setT(new Tea());
teaCup.setT(new GreenTea());
teaCup.setT(new BlackTea());

同时调用 get() 方法的值只能用上界去接收,因为无论里面是红茶、绿茶它都是“茶”

Tea tea = teaCup.getT()

于是非常神奇的事情出现了,这个 teaCup 就变成了一个只能 get 不能 set 的一个茶杯

所以我们得出结论:

上界 <? extends T> 不能往里存,只能往外取

现在我们再定义几个类

public class Drinks {}

public class Tea extends Drinks {}

public class Coffee extends Drinks {}

public class Latte extends Coffee {}

public class Mocha extends Coffee {}

<? extends T> 表示的就是

现在再来看下界通配符 <? super T>,它表示的是 T 或者 T 的所有父类

先说结论

下界 <? super T> 可以往里存,往外取只能用 Object 接收

因为往外取的时候不能确定具体的类型,所以无法用 Tea 或者 Drinks 来接收,乍一看上图能用 Drinks 来接收,实则不能(若 Drinks 有父类),所以只能用最顶端的父类 Object 来接收

往里存的时候只能存 Tea 以及 Tea 的子类,因为下界规定了元素最小粒度的下限,所以无论是 Tea、GreenTea、BlackTea 都是茶(Tea),也都是饮品(Drinks),所以往里存没有问题

PECS原则

PECS(Producer Extends Consumer Super)原则