Java 泛型 关于 ? extends super

  1. 说明
  2. 泛型的简单使用
  3. 泛型不变
  4. extends 泛型协变
  5. super 泛型逆变
  6. 结束说明
  7. 逆变协变优点

说明

  • ? extends T:声明上界,表示参数化的类型可能是所指定的 T 类型,或者是此类型的任意子类型。最终子类型:未知。
  • ? super T:声明下界,表示参数化的类型可能是所指定的 T 类型,或者是此类型的任意父类型。最终父类型:已知——Object。
  • Java 中泛型不变:假设有 A extends B,但 List 和 List 不存在型变关系。

泛型的简单使用

泛型不变

class A { }
class B extends A { }
List<A> list1 = new ArrayList<A>(); // 泛型不变
list1.add(new A());
list1.add(new B());
A a = list1.get(1);
List<A> list2 = new ArrayList<B>(); // 编译错误,泛型不变,也就不支持协变(类似多态)

extends 泛型协变

class A { }
class B extends A { }

List<? extends A> list1 = new ArrayList<B>(); // 协变——父类引用指向子类
list1.add(new Object()); // 错误,容器不可写,不能放入任何值(null 除外)
A a = list1.get(1); // work 可读,且有泛型

集合可读、不可写,集合泛型协变。

super 泛型逆变

class A {}
class B extends A {}

List<? super B> list = new ArrayList<A>(); // 逆变——子类引用指向父类
list.add(new A()); // 编译错误,集合中放入的元素类型只能为 B 及 B 子类型
list.add(new B()); // work
Object b = list.get(0); // work 可读,但无类型都是 Object

集合可读 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>();