Java泛型食用筆記(四) -- 通配符

Java泛型食用筆記(四) -- 通配符


1. 三種通配符

通配符為一個泛型類所指定的類型集合提供了一個有用的類型范圍,Java 里有三種通配符:

  • 無限定通配符, <?>
  • 上界限定符, <? extends Number>
  • 下界限定符, <? super Number>

上界限定符接受 extends 后面類的本身與其子類, 下界限定符接受 super 后面類的本身與其父類。無限定通配符接受任何類。

2. 無限定通配符

無限定通配符表示匹配任意類。ArrayList<?>ArrayListArrayList<Object> 看上去功能有點類似,但實際卻不一樣。 ArrayList<?> 是任意 ArrayList<T> 的超類,而我們知道 ArrayList<Object> 并不是。ArrayList<?> 雖然可以匹配任何類,我們并不知道那個類的類型,但我們知道里面的所有元素都有相同的類。而原始類 ArrayList 可以添加任意不同類型的元素,編譯器并不能進行類型判斷,但運行的時候可能會拋出異常。ArrayList<Object> 明確的告訴我們可以添加任何類型的對象。

看一個無限定通配符例子

public class GenericTest07 {
    public static void printCollection(Collection<Object> col1) {
        for (Object obj : col1) {
            System.out.println(obj);
        }
    }

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        printCollection(list);  // compile error
    }
}

由于 ArrayList<Integer> 不是 Collection<Object> 的子類,故編譯不能通過。改用無限定通配符即可編譯通過:

public class GenericTest07 {
    public static void printCollection(Collection<?> col1) {
        for (Object obj : col1) {
            System.out.println(obj);
        }
    }

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        printCollection(list);  
    }
}

Collection<?> 是任意 T ArrayList<T> 的超類,可以編譯通過。但是,在printCollection 方法中,卻不能使用任何帶有類型參數(shù)的方法,比如 add(T t), 而 remove(Object obj) contains(Object obj) 等方法都是可以調(diào)用的。

再看一個例子:

public class GenericTest08 {
    public static void reBox(Box<?> box) {
        System.out.println(box.put(box.get()));
    }
}

public interface Box<T> {
    public T get();
    public void put(T t);
}

這段代碼初看應(yīng)該是 work 的,然而事實可能要讓你失望了,編譯會提示類型不兼容的錯誤。事實上,你根本就不能在此處調(diào)用 box.put() 方法,因為box.put() 的形參類型是未知的,編譯器不能檢驗你的實參類型是否與形參類型一致。

有沒有辦法實現(xiàn)這個邏輯的,我們需要借助一個輔助方法。

public class GenericTest08 {
    public static void reBox(Box<?> box) {
        reboxHelper(box);
    }

    private static <V> void reboxHelper(Box<V> box) {
        box.put(box.get());
    }
}

reboxHelper 方法幫助編譯器保留一部分類型信息。

上界限定符

我們知道,和數(shù)組不一樣,泛型并不是協(xié)變的。比如 DogAnimal 的子類,那 Dog[] 也是 Animal[] 的子類,但 List<Dog> 并不是 List<Animal> 的子類。這個時候上界限定符的作用就體現(xiàn)出來了,寫法是 List<? extends Animal>,可以解釋為“可以放入任何 Animal 及其子類的列表”,之前講到泛型編譯后會抹除類型參數(shù)成 Object 類型,當你使用上界限定符后,就抹除成上界。

看代碼

public class GenericTest {
    public static void main(String[] args) {
        ArrayList<Integer> holder = new ArrayList<Integer>();
        holder.add(new Integer(1));

        // ArrayList<Number> numHolder = holder;  // 1. compile failed. ArrayList<Number> 不是 ArrayList<Integer> 的父類
        ArrayList<? extends Number> numHolder = holder; // 2. ok
        Number num = numHolder.get(0);  // 3. ok. return Number
        numHolder.contains(new Integer(2)); // 4. ok
        // numHolder.add(new Integer(2));  // 5. compile error
    }
}

1 處編譯錯誤是因為泛型不是協(xié)變;Integer 是 Number 子類,故 2 處正確;3 處正確,正如上段所說,上界限定符抹除成上屆,返回類型為 Number;4 處 ok,是因為 contains 方法接受的是 Object 類。5 處需要稍微注意,add 的形參類型也是 <? extends Number>,編譯器只能知道類型是 Number 的子類,并不能確定具體類型是什么,因此無法驗證類型的安全性。

下界限定符

下界限定符也稱為超類通配符,寫法是 List<? super Dog>, 列表可以接受 Dog 類型及其父類對象。
上一接的第 5 處,如果向往里添加元素,需要使用下界限定符。

public void writeTo(List<? super Integer> list) {
    list.add(new Integer(2));  // ok
}

<? super Integer> 說明類型參數(shù)一定是 Integer 的父類。因此向里添加 Integer 類或其子類的對象一定是安全的。

PECS 原則

根據(jù)上面的例子,我們看到,使用上界限定符定義的類,可以向外提供東西,也就是說作為 Producer。使用下界限定符定義的類,可以作為 Consumer 接收外部往自身添加東西。

總結(jié)起來就是, "Producer Extends, Consumer Super":

  • "Producer Extends" - 如果你需要一個只讀類型,用它來produce T,那么使用<? extends T>
  • "Consumer Super" - 如果你需要一個只寫類型,用它來consume T,那么使用<? super T>
  • 如果需要同時讀取以及寫入,那么我們就不能使用通配符了

下面一個方法同時涉及了這兩條規(guī)則。

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));
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容