天天看點

Java範型中 ? extends T 和 ? super T 的差別extendsSuperPECSextendssuper

前言:向上轉型是安全的,向下轉型是不安全的,除非你知道List中的真實類型,否則向下轉型就會報錯。

extends

List<? extends Number> foo3意味着下面的指派語句都是合法的:

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

給定上述可能的指派語句,能保證你從

List foo3

中取出什麼樣類型的對象?

  • 你可以讀取一個

    Number

    對象,因為上面任意一個list都包含

    Number

    對象或者

    Number

    子類的對象(上面的Number、Integer、Double都可以轉型成Number,并且是安全的,是以讀取總是可以的)。如下代碼就不會報錯:
List<? extends Number> foo4 = new ArrayList<Integer>();
        Number number = foo4.get(0);
           
  • 你不能讀取一個

    Integer

    對象,因為

    foo3

    可能指向的是

    List<Double>

    (與其運作時發現Double轉成Integer報錯,不如編譯時就不讓從

    foo3

    中取

    Integer

    對象)。如下代碼編譯時會報

    Incompatible types

    錯的:
List<? extends Number> foo4 = new ArrayList<Integer>();
        Integer number = foo4.get(0);
           

因為編譯的時候編譯器隻知道foo4引用是一個List<? extends Number>,要到運作時才會綁定到new ArrayList<Integer>(),是以編譯的時候是無法判斷foo4指向的List中到底是什麼類型,唯一能确定的就是這個類型是Number的子類(或者就是Number類)。

  • 你也不能讀取一個

    Double

    對象,因為

    foo3

    可能指向的是

    List<Integer>

2.寫入

給定上述可能的指派語句,你能往

List foo3

中添加什麼類型的對象進而保證它對于所有可能的

ArrayList

都是合法的呢?

  • 你不能添加一個

    Integer

    對象,因為

    foo3

    可能指向的是

    List<Double>

    。如下代碼是會編譯報錯的:
List<? extends Number> foo4 = new ArrayList<Integer>();
        foo4.add(new Integer(1));
           

因為編譯期間是無法知道foo4指向的ArrayList中到底放的是什麼類型,隻有到運作時才知道(就是Java所謂的晚綁定或運作時綁定)。與其到運作時發現往一個ArrayList<Double>中add一個Integer導緻抛出類型轉換異常,倒不如編譯時就報錯,即使ArrayList中放的就是Integer類型。

  • 你不能添加一個

    Double

    對象,因為

    foo3

    可能指向的是

    List<Integer>

  • 你不能添加一個

    Number

    對象,因為

    foo3

    可能指向的是

    List<Integer>

總結一下:你不能往

List<? extends T>

中添加任何對象,因為你不能保證

List

真正指向哪個類型,是以不能确定添加的對象就是

List

所能接受的類型。能保證的,僅僅是你可以從

List

中讀取的時候,你獲得的肯定是一個

T

類型的對象(即使是

T

類型的子類對象也是

T

類型的)。

Super

現在考慮

List<? super T>

包含通配符的聲明

List<? super Integer> foo3

意味着下面任何一個指派語句都是合法的:

List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer
           
1.讀取

給定上述可能的指派語句,當讀取

List foo3

中的元素的時候,你能保證接收到什麼類型的對象呢?

  • 你不能保證是一個

    Integer

    對象,因為

    foo3

    可能指向一個

    List<Number>

    或者

    List<Object>

  • 你不能保證是一個

    Number

    對象,因為

    foo3

    可能指向一個

    List<Object>

  • 你能保證的僅僅是它一定是一個

    Object

    類的執行個體或者

    Object

    子類的執行個體(但是你不知道到底是哪個子類)。
2.寫入

給定上述可能的指派語句,你能往

List foo3

中添加什麼類型的對象進而保證它對于所有可能的

ArrayList

都是合法的呢?

  • 你可以添加一個

    Integer

    執行個體,因為

    Integer

    類型對于上述所有的list都是合法的。
  • 你可以添加任何

    Integer

    子類的執行個體,因為一個

    Integer

    子類的執行個體都可以向上轉型成上面清單中的元素類型。
  • 你不可以添加

    Double

    類型,因為

    foo3

    可能指向的是

    ArrayList<Integer>

  • 你不可以添加

    Number

    類型,因為

    foo3

    可能指向的是

    ArrayList<Integer>

  • 你不可以添加

    Object

    類型,因為

    foo3

    可能指向的是

    ArrayList<Integer>

PECS

PECS是"Producer Extends,Consumer Super"(生産者用Extends,消費者用Super)的縮寫。

  • "Producer Extends"的意思是,如果你需要一個

    List

    去生産

    T

    類型values(也就是說你需要去list中讀取

    T

    類型執行個體),你需要聲明這個

    List

    中的元素為

    ? extends T

    ,例如

    List<? extends Integer>

    ,但是你不能往裡面添加元素。
  • "Consumer Super"的意思是,如果你需要一個

    List

    去消費

    T

    類型values(也就是說你需要往list中添加

    T

    類型執行個體),你需要聲明這個

    List

    中的元素為

    ? super T

    ,例如

    List<? super Integer>

    。但是不能保證你從這個list中讀取出來對象類型。
  • 如果你既需要往list中寫,也需要從list中讀,那麼你就不能用通配符

    ?

    ,必須用精确的類型,比如

    List<Integer>

  • 可以參考JDK源碼中的Collections類的copy方法,來了解PECS,源碼在文末有。

原文連結:

https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java

原文内容如下:

extends

The wildcard declaration of List<? extends Number> foo3 means that any of these are legal assignments:

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number
           
  1. Reading - Given the above possible assignments, what type of object are you guaranteed to read from List foo3:
  • You can read a Number because any of the lists that could be assigned to foo3 contain a Number or a subclass of Number.
  • You can’t read an Integer because foo3 could be pointing at a List.
  • You can’t read a Double because foo3 could be pointing at a List.
  1. Writing - Given the above possible assignments, what type of object could you add to List foo3 that would be legal for all the above possible ArrayList assignments:
  • You can’t add an Integer because foo3 could be pointing at a List.
  • You can’t add a Double because foo3 could be pointing at a List.
  • You can’t add a Number because foo3 could be pointing at a List.
  • You can’t add any object to List<? extends T> because you can’t guarantee what kind of List it is really pointing to, so you can’t guarantee that the object is allowed in that List. The only “guarantee” is that you can only read from it and you’ll get a T or subclass of T.

super

Now consider List <? super T>.

The wildcard declaration of List<? super Integer> foo3 means that any of these are legal assignments:

List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer
           
  1. Reading - Given the above possible assignments, what type of object are you guaranteed to receive when you read from List foo3:
  • You aren’t guaranteed an Integer because foo3 could be pointing at a List or List.
  • You aren’t guaranteed a Number because foo3 could be pointing at a List.
  • The only guarantee is that you will get an instance of an Object or subclass of Object (but you don’t know what subclass).
  1. Writing - Given the above possible assignments, what type of object could you add to List foo3 that would be legal for all the above possible ArrayList assignments:
  • You can add an Integer because an Integer is allowed in any of above lists.
  • You can add an instance of a subclass of Integer because an instance of a subclass of Integer is allowed in any of the above lists.
  • You can’t add a Double because foo3 could be pointing at an ArrayList.
  • You can’t add a Number because foo3 could be pointing at an ArrayList.
  • You can’t add an Object because foo3 could be pointing at an ArrayList.

PECS

Remember PECS: “Producer Extends, Consumer Super”.

  • “Producer Extends” - If you need a List to produce T values (you want to read Ts from the list), you need to declare it with ? extends T, e.g. List<? extends Integer>. But you cannot add to this list.
  • “Consumer Super” - If you need a List to consume T values (you want to write Ts into the list), you need to declare it with ? super T, e.g. List<? super Integer>. But there are no guarantees what type of object you may read from this list.
  • If you need to both read from and write to a list, you need to declare it exactly with no wildcards, e.g. List.

Example

Note this example from the Java Generics FAQ. Note how the source list src (the producing list) uses extends, and the destination list dest (the consuming list) uses super:

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

另外,可以參考JDK中的Collections類的copy方法,有助于記住PECS規則:

/**
     * Copies all of the elements from one list into another.  After the
     * operation, the index of each copied element in the destination list
     * will be identical to its index in the source list.  The destination
     * list must be at least as long as the source list.  If it is longer, the
     * remaining elements in the destination list are unaffected. <p>
     *
     * This method runs in linear time.
     *
     * @param  <T> the class of the objects in the lists
     * @param  dest The destination list.
     * @param  src The source list.
     * @throws IndexOutOfBoundsException if the destination list is too small
     *         to contain the entire source List.
     * @throws UnsupportedOperationException if the destination list's
     *         list-iterator does not support the <tt>set</tt> operation.
     */
    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://www.cnblogs.com/suxuan/p/4970467.html

前言:向上轉型是安全的,向下轉型是不安全的,除非你知道List中的真實類型,否則向下轉型就會報錯。

extends

List<? extends Number> foo3意味着下面的指派語句都是合法的:

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

給定上述可能的指派語句,能保證你從

List foo3

中取出什麼樣類型的對象?

  • 你可以讀取一個

    Number

    對象,因為上面任意一個list都包含

    Number

    對象或者

    Number

    子類的對象(上面的Number、Integer、Double都可以轉型成Number,并且是安全的,是以讀取總是可以的)。如下代碼就不會報錯:
List<? extends Number> foo4 = new ArrayList<Integer>();
        Number number = foo4.get(0);
           
  • 你不能讀取一個

    Integer

    對象,因為

    foo3

    可能指向的是

    List<Double>

    (與其運作時發現Double轉成Integer報錯,不如編譯時就不讓從

    foo3

    中取

    Integer

    對象)。如下代碼編譯時會報

    Incompatible types

    錯的:
List<? extends Number> foo4 = new ArrayList<Integer>();
        Integer number = foo4.get(0);
           

因為編譯的時候編譯器隻知道foo4引用是一個List<? extends Number>,要到運作時才會綁定到new ArrayList<Integer>(),是以編譯的時候是無法判斷foo4指向的List中到底是什麼類型,唯一能确定的就是這個類型是Number的子類(或者就是Number類)。

  • 你也不能讀取一個

    Double

    對象,因為

    foo3

    可能指向的是

    List<Integer>

2.寫入

給定上述可能的指派語句,你能往

List foo3

中添加什麼類型的對象進而保證它對于所有可能的

ArrayList

都是合法的呢?

  • 你不能添加一個

    Integer

    對象,因為

    foo3

    可能指向的是

    List<Double>

    。如下代碼是會編譯報錯的:
List<? extends Number> foo4 = new ArrayList<Integer>();
        foo4.add(new Integer(1));
           

因為編譯期間是無法知道foo4指向的ArrayList中到底放的是什麼類型,隻有到運作時才知道(就是Java所謂的晚綁定或運作時綁定)。與其到運作時發現往一個ArrayList<Double>中add一個Integer導緻抛出類型轉換異常,倒不如編譯時就報錯,即使ArrayList中放的就是Integer類型。

  • 你不能添加一個

    Double

    對象,因為

    foo3

    可能指向的是

    List<Integer>

  • 你不能添加一個

    Number

    對象,因為

    foo3

    可能指向的是

    List<Integer>

總結一下:你不能往

List<? extends T>

中添加任何對象,因為你不能保證

List

真正指向哪個類型,是以不能确定添加的對象就是

List

所能接受的類型。能保證的,僅僅是你可以從

List

中讀取的時候,你獲得的肯定是一個

T

類型的對象(即使是

T

類型的子類對象也是

T

類型的)。

Super

現在考慮

List<? super T>

包含通配符的聲明

List<? super Integer> foo3

意味着下面任何一個指派語句都是合法的:

List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer
           
1.讀取

給定上述可能的指派語句,當讀取

List foo3

中的元素的時候,你能保證接收到什麼類型的對象呢?

  • 你不能保證是一個

    Integer

    對象,因為

    foo3

    可能指向一個

    List<Number>

    或者

    List<Object>

  • 你不能保證是一個

    Number

    對象,因為

    foo3

    可能指向一個

    List<Object>

  • 你能保證的僅僅是它一定是一個

    Object

    類的執行個體或者

    Object

    子類的執行個體(但是你不知道到底是哪個子類)。
2.寫入

給定上述可能的指派語句,你能往

List foo3

中添加什麼類型的對象進而保證它對于所有可能的

ArrayList

都是合法的呢?

  • 你可以添加一個

    Integer

    執行個體,因為

    Integer

    類型對于上述所有的list都是合法的。
  • 你可以添加任何

    Integer

    子類的執行個體,因為一個

    Integer

    子類的執行個體都可以向上轉型成上面清單中的元素類型。
  • 你不可以添加

    Double

    類型,因為

    foo3

    可能指向的是

    ArrayList<Integer>

  • 你不可以添加

    Number

    類型,因為

    foo3

    可能指向的是

    ArrayList<Integer>

  • 你不可以添加

    Object

    類型,因為

    foo3

    可能指向的是

    ArrayList<Integer>

PECS

PECS是"Producer Extends,Consumer Super"(生産者用Extends,消費者用Super)的縮寫。

  • "Producer Extends"的意思是,如果你需要一個

    List

    去生産

    T

    類型values(也就是說你需要去list中讀取

    T

    類型執行個體),你需要聲明這個

    List

    中的元素為

    ? extends T

    ,例如

    List<? extends Integer>

    ,但是你不能往裡面添加元素。
  • "Consumer Super"的意思是,如果你需要一個

    List

    去消費

    T

    類型values(也就是說你需要往list中添加

    T

    類型執行個體),你需要聲明這個

    List

    中的元素為

    ? super T

    ,例如

    List<? super Integer>

    。但是不能保證你從這個list中讀取出來對象類型。
  • 如果你既需要往list中寫,也需要從list中讀,那麼你就不能用通配符

    ?

    ,必須用精确的類型,比如

    List<Integer>

  • 可以參考JDK源碼中的Collections類的copy方法,來了解PECS,源碼在文末有。

原文連結:

https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java

原文内容如下:

extends

The wildcard declaration of List<? extends Number> foo3 means that any of these are legal assignments:

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number
           
  1. Reading - Given the above possible assignments, what type of object are you guaranteed to read from List foo3:
  • You can read a Number because any of the lists that could be assigned to foo3 contain a Number or a subclass of Number.
  • You can’t read an Integer because foo3 could be pointing at a List.
  • You can’t read a Double because foo3 could be pointing at a List.
  1. Writing - Given the above possible assignments, what type of object could you add to List foo3 that would be legal for all the above possible ArrayList assignments:
  • You can’t add an Integer because foo3 could be pointing at a List.
  • You can’t add a Double because foo3 could be pointing at a List.
  • You can’t add a Number because foo3 could be pointing at a List.
  • You can’t add any object to List<? extends T> because you can’t guarantee what kind of List it is really pointing to, so you can’t guarantee that the object is allowed in that List. The only “guarantee” is that you can only read from it and you’ll get a T or subclass of T.

super

Now consider List <? super T>.

The wildcard declaration of List<? super Integer> foo3 means that any of these are legal assignments:

List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer
           
  1. Reading - Given the above possible assignments, what type of object are you guaranteed to receive when you read from List foo3:
  • You aren’t guaranteed an Integer because foo3 could be pointing at a List or List.
  • You aren’t guaranteed a Number because foo3 could be pointing at a List.
  • The only guarantee is that you will get an instance of an Object or subclass of Object (but you don’t know what subclass).
  1. Writing - Given the above possible assignments, what type of object could you add to List foo3 that would be legal for all the above possible ArrayList assignments:
  • You can add an Integer because an Integer is allowed in any of above lists.
  • You can add an instance of a subclass of Integer because an instance of a subclass of Integer is allowed in any of the above lists.
  • You can’t add a Double because foo3 could be pointing at an ArrayList.
  • You can’t add a Number because foo3 could be pointing at an ArrayList.
  • You can’t add an Object because foo3 could be pointing at an ArrayList.

PECS

Remember PECS: “Producer Extends, Consumer Super”.

  • “Producer Extends” - If you need a List to produce T values (you want to read Ts from the list), you need to declare it with ? extends T, e.g. List<? extends Integer>. But you cannot add to this list.
  • “Consumer Super” - If you need a List to consume T values (you want to write Ts into the list), you need to declare it with ? super T, e.g. List<? super Integer>. But there are no guarantees what type of object you may read from this list.
  • If you need to both read from and write to a list, you need to declare it exactly with no wildcards, e.g. List.

Example

Note this example from the Java Generics FAQ. Note how the source list src (the producing list) uses extends, and the destination list dest (the consuming list) uses super:

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

另外,可以參考JDK中的Collections類的copy方法,有助于記住PECS規則: