1

根据 Joshua Bloch 的“Effective Java”一书,有一条关于如何/何时在泛型中使用有界通配符的规则。这条规则就是 PECS(Producer-Extends,Comsumer-Super)。当我研究以下示例时:

Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = ... ;
numberStack.pushAll(integers);

我知道这条规则非常适合这个例子。我必须将该方法声明pushAll为以下示例:

// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
    for (E e : src)
    {
       push(e);
    }  
}

但是,如果我有以下示例,会发生什么?

Stack<Integer> integerStack = new Stack<Integer>();
Iterable<Number> numbers = ... ;
integerStack.pushAll(numbers);

我必须声明pushAll如下:

public void pushAll(Iterable<? super E> src) {
    for (E e : src)
    {  
        push(e);
    }
}

根据PECS规则,上述声明是错误的。但我想要 a Stackof Integers 并传递给这个Stacka Number。为什么不去做呢?
为什么我应该总是使用extends关键字?为什么使用super是错误的?
当然,同样代表消费者的观点。为什么消费者应该永远是super


PS:更具体地说,您可以在参考书的“第 28 项”部分找到上述示例。

4

5 回答 5

3

当您声明 a 时,Stack<Foo>您的意思是 Stack of Foos 或 Foo 的子类。例如,您希望能够将 a 放入 aStringStack<Object>。另一种方式是不正确的,您不应该能够在Stack<String>.

在您的示例中,您声明了一个Stack<Integer>. 您应该能够将整数放入此堆栈中,但不能将其他数字(如 Double)放入此堆栈中,如果您声明了参数,您会这样做<? super E>。这就是为什么 put-method 应该有一个 type 的参数<? extends E>

于 2013-06-05T11:21:39.437 回答
1

尝试将任意数字存储在堆栈中是不可能的,因为数字可能不是整数。所以你的例子没有多大意义。

当对象作为消费者时,您将使用 super,即当对象的泛型类型的实例作为参数传递给对象的方法时。例如:

 Collections.sort(List<T>, Comparator<? super T>)

在此示例中, sort 方法从集合中获取 T 个实例,并将它们作为参数传递给compare(T o1, T o2)比较器。

将此与您的第一个示例进行对比,其中 Iterablesrc是生产者。该pushAll()方法调用 Iterable 的一个方法,该方法产生(即返回)T 的实例。在这种情况下,iterable 是一个生产者,因此使用? extends T

于 2013-06-05T11:24:54.440 回答
0

首先要注意的是 Integer 扩展了 Number,因此您不应该将 Number 对象推入 Integer 堆栈。但是,第一个示例将使用 Integers、Floats、BigDecimal 和所有其他 Number 子类。

于 2013-06-05T11:25:22.493 回答
0

你的例子没有多大意义。类似的构造<? extends Number>意味着 Number 和所有继承自 Number 的类型都是允许的。因此,您定义了上下边界,从类型 Number 到最具体的类型。反过来,<? super Number>意味着 Number 及其任何超类型都是允许的。由于 Number 扩展了 Object 并实现了 Serializable,因此允许以下三种类型:

  1. java.lang.Number
  2. java.lang.Object
  3. java.io.Serializable

在您的示例中,您声明了泛型类型Stack<Integer>。让我们考虑以下内容。

  1. 您的 Stack 永远无法容纳任何超类型Integer 的项目
  2. 您的 Stack 永远无法保存 Integer 的任何子类型的项目,因为 Integer 类是最终类,因此它不能被子类化。

所以,如果你想声明泛型类型Stack<Integer>,你的迭代是类型的Iterable<Integer>,因此你的 Stack只能保存 Integer 类型的项目。您对助记符PECS完全正确,但这仅在您选择了具有至少一种超类型和至少一种子类型的具体类型时才有效。

于 2013-06-05T11:51:17.610 回答
0

在该pushAll方法中,您不是传递 type E,而是传递任何扩展的类型E。因此,您可以传递任何扩展的类型,而不是传递一个Iterableof s 。NumberIterableNumber

原始示例使用了一个Number类型,因为您可以传递任何属于Number、like 等Integer的子类的类型BigDecimal

在您的示例中,您正在以相反的方式进行操作。您正在使用Integer声明您的Stack. 因此,pushAll将只能接受那些由 扩展的类Integer。您将无法使用Numbers(或任何其他类,因为Integer是最终类)。

于 2013-06-05T11:22:22.820 回答