1. 程式人生 > >Java中 ? extends T 和 ? super T 的區別

Java中 ? extends T 和 ? super T 的區別

前言:向上轉型是安全的,向下轉型是不安全的,除非你知道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"的縮寫。

  • "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>

原文連結:
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)); 
  } 
}