【The Java™ Tutorials】【Generics】7. Wildcards

在泛型代码中,?被称为通配符,用于表示未知类型。可被用于: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和List作为参数。因为List不是List的子类,所以不能简单的把函数定义为foo(List list)。这个时候Upper Bounded Wildcards就派上了用场。

public static void foo(List list) { /* ... */ }

这个时候list的元素可以调用Number中定义的方法:

public static double foo(List 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 list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}
 
 

printList可以打印任意类型的list,但是printList不能接受List, List等类型,因为它们不是List的子类。用unbounded wildcards改写printList就可以解决上述问题:

public static void printList(List list) {
    for (Object elem: list)
        System.out.print(elem + " ");
    System.out.println();
}

注意:List和List是不同的,你可以添加任意类实例到List列表中,但是你只能添加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, List, and List — anything that can hold Integer values.

public static void addNumbers(List 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的子类。那他们之间有什么关系呢:

List和List的公共父类

除此之外,还有以下层级关系:


【The Java™ Tutorials】【Generics】7. Wildcards_第2张图片
A hierarchy of several generic List class declarations

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 l1, List 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中的元素显然是不合理的。

你可能感兴趣的:(【The Java™ Tutorials】【Generics】7. Wildcards)