在泛型代码中,?被称为通配符,用于表示未知类型。可被用于: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
我们来看这么一个场景:你希望函数foo可以接受List<Number>和List<Number的子类>作为参数。因为List<Number的子类>不是List<Number>的子类,所以不能简单的把函数定义为foo(List<Number> list)。这个时候Upper Bounded Wildcards就派上了用场。
public static void foo(List<? extends Number> list) { /* ... */ }
这个时候list的元素可以调用Number中定义的方法:
public static double foo(List<? extends Number> list) {
double s = 0.0;
for (Number n : list)
s += n.doubleValue();
return s;
}
这个时候foo可以接收List<Number的子类>作为参数:
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
一个常见的unbounded wildcards的例子是Class<?>
。什么情况会用到unbounded wildcards呢:
- 你只需要用到
Object
提供的方法 - 你的代码和参数类型没有关系,比如: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>等类型,因为它们不是List<Object>的子类。用unbounded wildcards改写printList就可以解决上述问题:
public static void printList(List<?> list) {
for (Object elem: list)
System.out.print(elem + " ");
System.out.println();
}
注意:List<Object>和List<?>是不同的,你可以添加任意类实例到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
假设有以下两个类:
class A { /* ... */ }
class B extends A { /* ... */ }
像下面这么写是合理的:
B b = new B();
A a = b;
但是如果像下面这么写就会报错:
List<B> lb = new ArrayList<>();
List<A> la = lb; // compile-time error
也就是说虽然B是A的子类,但是List<B>却不是List<A>的子类。那他们之间有什么关系呢:
除此之外,还有以下层级关系:
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".
我们先来看一个WildcardError
的例子:
import java.util.List;
public class WildcardError {
void foo(List<?> i) {
i.set(0, i.get(0));
}
}
编译上面的代码有出现如下错误:Error:(9, 22) java: 不兼容的类型: java.lang.Object无法转换为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));
}
}
现在来看一段更复杂的情况:
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
}
}
编译上面的代码会得到如下错误:
Error:(10, 25) java: 不兼容的类型: java.lang.Number无法转换为capture#1, 共 ? extends java.lang.Number
Error:(13, 19) java: 不兼容的类型: java.lang.Number无法转换为capture#2, 共 ? extends java.lang.Number
我们应该注意到,这种编译错误是合理的。假如l1是Integer的List,l2是Double的List,交换这个两个List中的元素显然是不合理的。