1. 程式人生 > >在java的泛型中super和extends的區別

在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());
        }
    }
}