在java的泛型中super和extends的区别

环境

java:1.7+

前言

主要讲的是<? super T> 和 <? extends T>的区别!
这个是我在打算封装一段通用代码时,发现经常用的<? extends T>,网上搜索时,发现其总是要和<? super T>进行比较。
stackoverflow中看到一个很好的解释,这里记下笔记;

extends

声明通配符List<? extends Number> foo3,其意味着有如下的可能:

// Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Number>();  
// Integer extends Number
List<? extends Number> foo3 = new ArrayList<Integer>(); 
// Double extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  

读取或者获取:

考虑到上面的情况,你可以确定在List 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>,因为你不能确定List真正指向的是哪个,所以你不能保证该列表中允许哪个对象。你能确定的只有,你能从中得到T或者T的子类。

super

现在讨论下:List <? super T>

声明通配符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>();   

读取

基于上面的情况,当从List foo3中读取时,你能确定收到什么样的类型对象:

  • 你不能保证得到Integer,因为foo3可能指向一个List<Number>或者List<Object>
  • 你不能保证得到Number,因为foo3可能指向一个一个List<Object>
  • 你只能保证得到,你将会得到一个Object或者Object子类(但是你不知道具体什么样的子类)。

写入

基于上面的情况,你可以添加什么类型的对象到List foo3中来满足上面的情况:

  • 你可以添加Integer,因为Integer满足上面的所有的情况
  • 你可以添加Integer的子类实例,因为该实例也满足上面的情况。
  • 你不能添加Double,因为foo3可能指向ArrayList<Integer>.
  • 你不能添加Number,因为foo3可能指向ArrayList<Integer>
  • 你不能添加Object,因为foo3可能指向ArrayList<Integer>

PECS

PECSProducer Extends, Consumer Super的缩写 :

  • Producer Extends 如果你需要一个List来生产T值(你想从list中读取T),你需要将其声明为?
    extends T
    ,例如:List<? extends Integer>。但是你不能使用add方法。

  • Consumer Super: 如果你需要一个List去消费T值(你想写入TList),你需要将其声明为? super T,例如:List<? super Integer>。但是你不能确定从list中读取到的是什么类型对象。

  • 如果你即想从list中读取又想去写入,那么你需要声明为一个具体的泛型,而不是通配符。
    例如:List<Integer>.

下面是同时使用extendssuper的情况。

public class Collections { 
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++) 
        dest.set(i, src.get(i)); 
  } 
}
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());
        }
    }
}

参考地址:
https://stackoverflow.com/a/4343547/6952713

猜你喜欢

转载自blog.csdn.net/u013066244/article/details/79985348