说明
- ? extends T:声明上界,表示参数化的类型可能是所指定的 T 类型,或者是此类型的任意子类型。最终子类型:未知。
- ? super T:声明下界,表示参数化的类型可能是所指定的 T 类型,或者是此类型的任意父类型。最终父类型:已知——Object。
- Java 中泛型不变:假设有 A extends B,但 List 和 List 不存在型变关系。
泛型的简单使用
泛型不变
|
extends 泛型协变
|
集合可读、不可写,集合泛型协变。
super 泛型逆变
|
集合可读 Object 、可写,集合泛型逆变。
结束说明
- ? extends T:针对返回值泛型使用(如,只读的消费者集合泛型),指定的 T 为集合元素的通用父类型,用于限定取出类型为 T 的子类型、打破泛型不变。
- ? super T:针对方法参数泛型使用(如,只写的生产者集合泛型),指定的 T 为集合元素的通用父类型,用于限定放入类型为 T 的子类型、打破泛型不变。
可以看出,感觉到extends 与 super 功能互补。extends 用于方法返回值,super 用于方法参数。
逆变协变优点
我们用 Java 对现实世界的水果进行简单的抽象,水果抽象为 Fruit,Apple等于 Fruit 存在继承关系。盛放水果的盘子 plate 被抽象为 List。
于是我们 OOP 代码抽象得到:class Fruit { }
class Apple extends Fruit { }
class Banana extends Fruit { }
class Watermelon extends Fruit { }
List<Fruit> plate1 = new ArrayList<Apple>(); // 编译错误
List<Fruit> plate2 = new ArrayList<Banana>(); // 编译错误
List<Fruit> plate3 = new ArrayList<Watermelon>(); // 编译错误
编译错误:
Java 中类型存在协变关系
但是 Java 中类型上泛型不存在协变关系,即 List!= ArrayList ,因此编译器提示泛型协变的编译错误。
我们利用上面学到的 ? extends 、? super 打破泛型不变的特性,提供泛型协变,提高代码的复用性:class A {}
class B extends A{}
class C extends A{}
// 协变,用作只读型容器————集合中元素都是 A 或 A 子类型
List<? extends A> plate1 = new ArrayList<A>();
List<? extends A> plate2 = new ArrayList<B>();
List<? extends A> plate3 = new ArrayList<C>();
// 逆变,用作只写型容器————集合中元素都是 B 或 B 父类型
List<? super B> plate4 = new ArrayList<A>();
// 上述结果
List<? extends Fruit> list = new ArrayList<Apple>();
List<? super Apple> list1 = new ArrayList<Fruit>();