1. 程式人生 > >Java中的逆變和協變

Java中的逆變和協變

原則 oid UC bject net 就是 pos ecs product

// public final class Integer extends Number  
Number num = new Integer(1);    
List<Number> list = new ArrayList<>();  
list.add(new Integer(3));  
ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch  
  
List<? extends Number> list = new ArrayList<Number>();  
list.add(
new Integer(1)); //error

為什麽Number的對象可以由Integer實例化,而ArrayList<Number>的對象卻不能由ArrayList<Integer>實例化?

list中的<? extends Number>聲明其元素是Number或Number的派生類,為什麽不能add Integer?

為了解決這些問題,需要了解Java中的逆變和協變以及泛型中通配符用法。

1、逆變和協變

  ava中String類型是繼承自Object的,姑且記做String ≦ Object,表示String是Object的子類型,String的對象可以賦給Object的對象。而Object的數組類型Object[],理解成是由Object構造出來的一種新的類型,可以認為是一種構造類型,記f(Object),那麽可以這麽來描述協變和逆變:
當A ≦ B時,如果有f(A) ≦ f(B),那麽f叫做協變;
當A ≦ B時,如果有f(B) ≦ f(A),那麽f叫做逆變;
如果上面兩種關系都不成立則叫做不可變。

2、泛型中的通配符實現逆變和協變

  JAVA中泛型是不變的,可有時需要實現逆變與協變,怎麽辦呢?這時就需要通配符?。
<? extends>實現了泛型的協變,比如:

 

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

  “? extends Number”則表示通配符”?”的上界為Number,換句話說就是,

  “? extends Number”可以代表Number或其子類,但代表不了Number的父類(如Object),因為通配符的上界是Number。
於是有“? extends Number” ≦ Number,則List<? extends Number> ≦ List< Number >。那麽就有:

  

List<? extends Number> list001 = new ArrayList<Integer>();  
List<? extends Number> list002 = new ArrayList<Float>();  

但是這裏不能向list001、list002添加除null以外的任意對象。可以這樣理解一下,List<Integer>可以添加Interger及其子類,List<Float>可以添加Float及其子類,List<Integer>、List<Float>都是List<? extends Animal>的子類型,如果能將Float的子類添加到List<? extends Animal>中,就說明Float的子類也是可以添加到List<Integer>中的,顯然是不可行。故java為了保護其類型一致,禁止向List<? extends Number>添加任意對象,不過卻可以添加null。

<? super>實現了泛型的逆變,比如:

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

“? super Number” 則表示通配符”?”的下界為Number。為了保護類型的一致性,因為“? super Number”可以是Object或其他Number的父類,因無法確定其類型,也就不能往List<? super Number >添加Number的任意父類對象。但是可以向List<? super Number >添加Number及其子類。

List<? super Number> list001 = new ArrayList<Number>();  
List<? super Number> list002 = new ArrayList<Object>();  
list001.add(new Integer(3));  
list002.add(new Integer(3));  

PECS原則
  生產者(Product)使用extends,消費者(Consumer)使用super。
 生產者使用extends
  如果需要一個列表提供T類型的元素(即想從列表中讀取T類型的元素),需要把這個列表聲明成<? extends T>,

  但是不能在該列表中添加任何元素。
 消費者使用super
  如果需要一個列表使用T類型的元素(即想把T類型的元素加入到列表中),需要把這個列表聲明成<? super T>

  比如List<? super Integer>,不能保證從中讀取到的元素類型。
 即是生產者也是消費者
    如果一個列表即要生產又要消費,不能使用泛型通配符聲明列表

Java.util.Collections的copy方法(JDK1.7)完美詮釋了PESC

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://blog.csdn.net/zero__007/article/details/52245475

Java中的逆變和協變