在泛型代碼中逢防,?被稱為通配符,用于表示未知類型∷┎猓可被用于:the type of a parameter, field, or local variable; sometimes as a return type;不可被用于: the type argument for a generic method invocation, a generic class instance creation, or a supertype府蛇。
Upper Bounded Wildcards
我們來看這么一個(gè)場景:你希望函數(shù)foo可以接受List<Number>和List<Number的子類>作為參數(shù)集索。因?yàn)長ist<Number的子類>不是List<Number>的子類,所以不能簡單的把函數(shù)定義為foo(List<Number> list)汇跨。這個(gè)時(shí)候Upper Bounded Wildcards就派上了用場务荆。
public static void foo(List<? extends Number> list) { /* ... */ }
這個(gè)時(shí)候list的元素可以調(diào)用Number中定義的方法:
public static double foo(List<? extends Number> list) {
double s = 0.0;
for (Number n : list)
s += n.doubleValue();
return s;
}
這個(gè)時(shí)候foo可以接收List<Number的子類>作為參數(shù):
List<Integer> li = Arrays.asList(1, 2, 3);
System.out.println("sum = " + sumOfList(li));
List<Double> ld = Arrays.asList(1.2, 2.3, 3.5);
System.out.println("sum = " + sumOfList(ld));
Unbounded Wildcards
一個(gè)常見的unbounded wildcards的例子是Class<?>
。什么情況會(huì)用到unbounded wildcards呢:
- 你只需要用到
Object
提供的方法 - 你的代碼和參數(shù)類型沒有關(guān)系穷遂,比如:List.size, List.clear函匕。
我們先來看下面這段代碼:
public static void printList(List<Object> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}
printList可以打印任意類型的list,但是printList不能接受List<Integer>, List<String>等類型蚪黑,因?yàn)樗鼈儾皇荓ist<Object>的子類盅惜。用unbounded wildcards改寫printList就可以解決上述問題:
public static void printList(List<?> list) {
for (Object elem: list)
System.out.print(elem + " ");
System.out.println();
}
注意:List<Object>和List<?>是不同的,你可以添加任意類實(shí)例到List<Object>列表中祠锣,但是你只能添加null到List<?>列表中酷窥。
Lower Bounded Wildcards
場景:Say you want to write a method that puts Integer objects into a list. To maximize flexibility, you would like the method to work on List<Integer>, List<Number>, and List<Object> — anything that can hold Integer values.
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
Wildcards and Subtyping
假設(shè)有以下兩個(gè)類:
class A { /* ... */ }
class B extends A { /* ... */ }
像下面這么寫是合理的:
B b = new B();
A a = b;
但是如果像下面這么寫就會(huì)報(bào)錯(cuò):
List<B> lb = new ArrayList<>();
List<A> la = lb; // compile-time error
也就是說雖然B是A的子類,但是List<B>卻不是List<A>的子類伴网。那他們之間有什么關(guān)系呢:
除此之外蓬推,還有以下層級(jí)關(guān)系:
Wildcard Capture and Helper Methods
In some cases, the compiler infers the type of a wildcard. For example, a list may be defined as List<?> but, when evaluating an expression, the compiler infers a particular type from the code. This scenario is known as wildcard capture.
For the most part, you don't need to worry about wildcard capture, except when you see an error message that contains the phrase "capture of".
我們先來看一個(gè)WildcardError
的例子:
import java.util.List;
public class WildcardError {
void foo(List<?> i) {
i.set(0, i.get(0));
}
}
編譯上面的代碼有出現(xiàn)如下錯(cuò)誤:Error:(9, 22) java: 不兼容的類型: java.lang.Object無法轉(zhuǎn)換為capture#1, 共 ?
可以用以下方法解決上述問題,編譯器可以推斷出T是capture#1:
public class WildcardFixed {
void foo(List<?> i) {
fooHelper(i);
}
// Helper method created so that the wildcard can be captured
// through type inference.
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0));
}
}
現(xiàn)在來看一段更復(fù)雜的情況:
import java.util.List;
public class WildcardErrorBad {
void swapFirst(List<? extends Number> l1, List<? extends Number> l2) {
Number temp = l1.get(0);
l1.set(0, l2.get(0)); // expected a CAP#1 extends Number,
// got a CAP#2 extends Number;
// same bound, but different types
l2.set(0, temp); // expected a CAP#1 extends Number,
// got a Number
}
}
編譯上面的代碼會(huì)得到如下錯(cuò)誤:
Error:(10, 25) java: 不兼容的類型: java.lang.Number無法轉(zhuǎn)換為capture#1, 共 ? extends java.lang.Number
Error:(13, 19) java: 不兼容的類型: java.lang.Number無法轉(zhuǎn)換為capture#2, 共 ? extends java.lang.Number
我們應(yīng)該注意到澡腾,這種編譯錯(cuò)誤是合理的沸伏。假如l1是Integer的List糕珊,l2是Double的List,交換這個(gè)兩個(gè)List中的元素顯然是不合理的毅糟。