在泛型代码中,?被称为通配符,用于表示未知类型。可被用于: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
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
List li = Arrays.asList(1, 2, 3);
System.out.println("sum = " + sumOfList(li));
List 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
printList可以打印任意类型的list,但是printList不能接受List
public static void printList(List> list) {
for (Object elem: list)
System.out.print(elem + " ");
System.out.println();
}
注意: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
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 lb = new ArrayList<>();
List la = lb; // compile-time error
也就是说虽然B是A的子类,但是List却不是List的子类。那他们之间有什么关系呢:
除此之外,还有以下层级关系:
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 void fooHelper(List 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中的元素显然是不合理的。