類型參數和通配符的選擇
December 23, 2018這篇文章是我在學習Item31的時候 自己提出來的問題 上網找到解答之後 覺得有必要記錄下來 才產生了這篇文章
問題
如同Item31最後一小章的問題
這兩個差在哪
public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list, int i, int j);
還有以下的其他聲明 差在哪
public boolean containsAll(Collection<?> c);
public <T> boolean containsAll(Collection<T> c);
public boolean addAll(Collection<? extends E> c);
public <T extends E> boolean addAll(Collection<T> c);
public static <T> void copy(List<T> dest, List<? extends T> src)
public static <T, S extends T> void copy(List<T> dest, List<S> src)
如果以上的聲明 你都知道差別以及使用時機 那讀者可以跳過這篇文章
類型參數和通配符
兩者區別與使用時機
1.通配符只能讀取 不能添加
所以要取決於你內部的實作 如果你需要在List裡面加一個東西 就不能用通配符
public static <E> void funct1(final List<E> list1, final E something) {
list1.add(something);
}
public static void funct2(final List<?> list, final Object something) {
list.add(something); // does not compile
}
記得: 能加進List<?>的只有null
2.如果你想要在一個聲明裡面表明兩個類別的關係 那就要用類型參數
比如說
public static <T extends Number> void copy(List<T> dest, List<T> src)
你要明確的表示dest跟src同一個類型 就只能用以上方式 以下方式沒用
public static void copy(List<? extends Number> dest, List<? extends Number> src)
這樣有可能dest是List<Integer>
但是src是List<Double>
3.你想要多重限制一個類型(Multiple bound) 那只能用類型參數
public static <T extends Comparable & Serializable> void copy()
以下方式不行
public static <? extends Comparable & Serializable> void copy()
4.你想支持類型下限的限制
你用類型參數 你就只能限制上限extends
public <T extends Integer> void genericExtend(List<T> list){}
不能限制下限
//Compile不過
public <T super Integer> void genericSuper(List<T> list){}
如果是wildcard 上下限都ok
public void wildCardSuper(List<? super Integer> list){}
public void wildCardExtend(List<? extends Integer> list){}
5.如果這個類型 在函數裡面也有用到 那就當然只能用類型參數
public void pushAll(Iterable<E> src) {
for (E e : src)
push(e);
}
6.如果一個聲明裡面 類型參數只出現過一次 那就改成通配符 這在Item31也有提到
結論
能用通配符就盡量用通配符 但如果你的函示需要
1.實作中需要寫入(消費者)
2.一個聲明裡面表明兩個類別的關係
3.多重限制(Multiple bound)
4.函數的實作本身 也需要使用到同一個類型
那你就必需要使用類型參數