原文點此連接javascript
使用通配符的緣由:Java中的數組是協變的,可是泛型不支持協變。php
首先了解下什麼是數組的協變,看下面的例子:java
Number[] nums = new Integer[10]; // OK
由於Integer是Number的子類,一個Integer對象也是一個Number對象,因此一個Integer的數組也是一個Number的數組,這就是數組的協變。數組
Java把數組設計成協變的,在必定程度上是有缺陷的。由於儘管把Integer[]賦值給Number[],Integer[]能夠向上轉型爲Number[],可是數據元素的實際類型是Integer,只能向數組中放入Integer或者Integer的子類。若是向數組中放入Number對象或者Number其餘子類的對象,對於編譯器來講也是能夠經過編譯的。可是運行時JVM可以知道數組元素的實際類型是Integer,當其它對象加入數組是就會拋出異常(java.lang.ArrayStoreException)。安全
泛型的設計目的之一就是保證了類型安全,讓這種運行時期的錯誤在編譯期就能發現,因此泛型是不支持協變的。例如:數據結構
List<Number> nums = new ArrayList<Integer>(); // incompatible types
當確實須要創建這種向上轉型的類型關係的時候,就須要用到泛型的通配符特性了。例如:測試
List<? extends Number> nums = new ArrayList<Integer>(); // OK
class-name<?> var-name
ui
public static void print(List<?> list) {
for (Object obj : list) {
System.out.println(o);
}
}
爲何要使用這樣脆弱的類型?它對於許多簡單的操做很是有用。例如 ,下面這個方法將用來測試一個 pair 是否包含一個 mill 引用,它不須要實際的類型。 public static boolean hasNulls (Pair<?> p) { return p.getFirstO = null | | p.getSecondO = null ; } 經過將 hasNulls 轉換成泛型方法,能夠避免使用通配符類型: public static <T> boolean hasNulls (Pair<T> p) 可是,帶有通配符的版本可讀性更強。
class-name<? extends superclass> var-name
spa
public static double sum(List<? extends Number> list) {
double s = 0.0;
for (Number num : list) {
s += num.doubleValue();
}
return s;
}
List<? extends Number> list = new ArrayList<Integer>(); // OK
List<? extends Number> list = new ArrayList<Object>(); // error
list.add(new Integer(1)); // error
list.add(null); // OK
Number n = list.get(0); // OK
Integer i = list.get(0); // error
public E get(int index) // 能夠調用 public int indexOf(Object o) // 能夠調用 public boolean add(E e) // 不能調用
class-name<? super subclass> var-name
設計
public static void writeTo(List<? super Integer> list) {
// ...
}
List<? super Number> list = new ArrayList<Number>(); // OK
List<? super Number> list = new ArrayList<Object>(); // OK
List<? super Number> list = new ArrayList<Integer>(); // error
list.add(new Integer(1)); // OK
list.add(new Object()); // error
Object obj = list.get(0); // OK
Integer i = list.get(0); // error
從上面上邊界限定的通配符和下邊界限定的通配符的特性,能夠知道:
簡而言之,上邊界限定(extends)的通配符適合於內容的獲取,而下邊界限定(super)的通配符更適合於內容的存入。因此就有了一個PECS原則來很好的解釋這兩種通配符的使用原則。
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));
}
}