泛型中 ? super T 和 ? extends T 的区别

<? super T>表示包括T在内的任何T的父类,<? extends T>表示包括T在内的任何T的子类,下面我们详细分析这两种通配符的区别

extends

List<? extends Number> foo3的通配符声明,意味着以下的赋值都是合法的:

// Number "extends" Number (in this context)

List<? extends Number> foo3 = new ArrayList<? extends Number>(); 

// Integer extends Number

List<? extends Number> foo3 = new ArrayList<? extends Integer>();

// Double extends Number

List<? extends Number> foo3 = new ArrayList<? extends Double>();

读取操作通过以上给定的赋值语句,一定能从foo3列表中读取到的元素的类型是什么?

  • 能读取到Number,因为以上的列表要么包含Number元素,要么包含Number的类元素。
  • 不能保证读取到Integer,因为foo3可能指向的是List<Double>。
  • 不能保证读取到Double,因为foo3可能指向的是List<Integer>。

写入操作通过以上给定的赋值语句,能把什么类型的数据合法插入到foo3中?

  • 不能插入一个Integer元素,因为foo3可能指向List<Double>。
  • 不能插入一个Double元素,因为foo3可能指向List<Integer>。
  • 不能插入一个Number元素,因为foo3可能指向List<Integer>。

不能往List<? extends T>中插入任何类型的对象,因为不能保证列表实际指向的类型是什么,并不能保证列表中实际存储什么类型的对象。唯一可以保证的是,可以从中读取到T或者T的子类。

super

List<? super Integer> foo3的通配符声明,意味着以下赋值是合法的:

// Integer is a "superclass" of Integer (in this context)

List<? super Integer> foo3 = new ArrayList<Integer>();

// Number is a superclass of Integer

List<? super Integer> foo3 = new ArrayList<Number>();

// Object is a superclass of Integer

List<? super Integer> foo3 = new ArrayList<Object>();

读取操作以上给定的赋值语句,一定能从foo3列表中读取到的元素是什么?

  • 不能保证读到Integer,因为foo3可能指向List<Number>或者List<Object>。
  • 不能保证读到Number,因为foo3可能指向List<Object>。

唯一可以保证的是,你可以读取到Object或者Object子类的对象,并不知道具体的子类是什么。

写入操作通过以上给定的赋值语句,能把什么类型的元素合法的插入到foo3中?

  • 能插入Integer对象,因为上述声明的列表都支持Integer。
  • 能插入Integer的子类对象,因为Integer的子类同时也是Integer,原因同上。
  • 不能插入Double对象,因为foo3可能指向ArrayList<Integer>。
  • 不能插入Number对象,因为foo3可能指向ArrayList<Integer>。
  • 不能插入Object对象,因为foo3可能指向ArrayList<Integer>。

PECS

请记住PECS原则:生产者(Producer)使用extends,消费者(Consumer)使用super。

(1)生产者使用extends
如果需要一个列表提供T类型的元素(即你想从列表中读取T类型的元素),你需要把这个列表声明成<? extends T>,比如 List<? extends Integer>,因此你不能往该列表中添加任何元素。
(2)消费者使用super
如果需要一个列表使用T类型的元素(即你想把T类型的元素加入到列表中),你需要把这个列表声明成<? super T>,比如 List<? super Integer>,因此你不能保证从中读取到的元素的类型。
(3)既是消费者也是生产者
如果一个列表既要消费,又要生产,那就不能使用泛型通配符声明列表,要使用明确的泛型,比如 List<Integer>。

以下是JDK8中java.util.Collections中copy的代码:

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    int srcSize = src.size();
    if (srcSize > dest.size())
        throw new IndexOutOfBoundsException("Source does not fit in dest");

    if (srcSize < COPY_THRESHOLD ||
        (src instanceof RandomAccess && dest instanceof RandomAccess)) {
        for (int i=0; i<srcSize; i++)
            dest.set(i, src.get(i));
    } else {
        ListIterator<? super T> di=dest.listIterator();
        ListIterator<? extends T> si=src.listIterator();
        for (int i=0; i<srcSize; i++) {
            di.next();
            di.set(si.next());
        }
    }
}

从代码中可以看到作为生产者的src的声明是List<? extends T>,作为消费者的dest的声明是List<? super T>。可以再用一句话简单的便于理解,假如T是Number,如果生产者生产Integer类型,那么消费者消费Object类型,明显消费者可以消费;如果生产者生产Object类型,那么消费者消费Integer类型,明确消费者不可以消费;可以看到Object > Number > Integer

泛型中 ? super T 和 ? extends T 的区别

: » 泛型中 ? super T 和 ? extends T 的区别

原创文章,作者:6024010,如若转载,请注明出处:https://blog.ytso.com/tech/java/251514.html

(0)
上一篇 2022年5月3日 03:41
下一篇 2022年5月3日 03:45

相关推荐

发表回复

登录后才能评论