Kotlin極簡教程中對? extends 和 ? super (out和int)的描述

PECS

如今問題來了:咱們何時用extends何時用super呢?《Effective Java》給出了答案:java

PECS: producer-extends, consumer-super安全

好比,一個簡單的Stack API:dom

public class Stack<E>{  
    public Stack();  
    public void push(E e):  
    public E pop();  
    public boolean isEmpty();  
}

要實現pushAll(Iterable<E> src)方法,將src的元素逐一入棧:spa

public void pushAll(Iterable<E> src){  
    for(E e : src)  
        push(e)  
}

假設有一個實例化Stack<Number>的對象stack,src有Iterable<Integer>與 Iterable<Float>;code

在調用pushAll方法時會發生type mismatch錯誤,由於Java中泛型是不可變的,Iterable<Integer>與 Iterable<Float>都不是Iterable<Number>的子類型。對象

所以,應改成ip

// Wildcard type for parameter that serves as an E producer  
public void pushAll(Iterable<? extends E> src) {  
    for (E e : src)   // out T, 從src中讀取數據,producer-extends
        push(e);  
}

要實現popAll(Collection<E> dst)方法,將Stack中的元素依次取出add到dst中,若是不用通配符實現:ci

// popAll method without wildcard type - deficient!  
public void popAll(Collection<E> dst) {  
    while (!isEmpty())  
        dst.add(pop());    
}

一樣地,假設有一個實例化Stack<Number>的對象stack,dst爲Collection<Object>;get

調用popAll方法是會發生type mismatch錯誤,由於Collection<Object>不是Collection<Number>的子類型。it

於是,應改成:

// Wildcard type for parameter that serves as an E consumer  
public void popAll(Collection<? super E> dst) {  
    while (!isEmpty())  
        dst.add(pop());   // in T, 向dst中寫入數據, consumer-super
}

Naftalin與Wadler將PECS稱爲 Get and Put Principle

java.util.Collectionscopy方法中(JDK1.7)完美地詮釋了PECS:

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();   // in T, 寫入dest數據
        ListIterator<? extends T> si=src.listIterator();   // out T, 讀取src數據
        for (int i=0; i<srcSize; i++) {  
            di.next();  
            di.set(si.next());  
        }  
    }  
}

6.3 Kotlin的泛型特點

正如上文所講的,在 Java 泛型裏,有通配符這種東西,咱們要用? extends T指定類型參數的上限,用 ? super T 指定類型參數的下限。

而Kotlin 拋棄了這個東西,引用了生產者和消費者的概念。也就是咱們前面講到的PECS。生產者就是咱們去讀取數據的對象,消費者則是咱們要寫入數據的對象。這兩個概念理解起來有點繞。

咱們用代碼示例簡單講解一下:

public static <T> void copy(List<? super T> dest, List<? extends T> src) {  
        ...
        ListIterator<? super T> di = dest.listIterator();   // in T, 寫入dest數據
        ListIterator<? extends T> si = src.listIterator();   // out T, 讀取src數據
         ...
}

List<? super T> dest是消費(方法產生)數據的對象,這些數據會寫入到該對象中,這些數據該對象被「吃掉」了(Kotlin中叫in T)。

List<? extends T> src是(爲方法)提供數據的對象。這些數據哪裏來的呢?就是經過src讀取得到的(Kotlin中叫out T)。

6.3.1 out T 與 in T

在Kotlin中,咱們把那些只能保證讀取數據時類型安全的對象叫作生產者,用 out T 標記;把那些只能保證寫入數據安全時類型安全的對象叫作消費者,用 in T 標記。

若是你以爲太晦澀難懂,就這麼記吧:

out T 等價於 ? extends T in T 等價於 ? super T 此外, 還有 * 等價於 ?

相關文章
相關標籤/搜索