Java泛型(有界通配符)

时间:2013-06-05 11:11:44

标签: java generics bounded-wildcard effective-java pecs

根据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 规则,上述声明是错误的。但我想要Stack IntegerStack并传递给Number一个extends。为什么不这样做?
为什么我总是要使用super关键字?为什么使用super是错误的?
当然,同样代表 comsumer 的观点。为什么消费者应始终为{{1}}?


PS:更具体地说,你可以在推荐书的“ Item 28 ”部分找到上面这个例子。

5 个答案:

答案 0 :(得分:3)

当你声明Stack<Foo>时,你指的是一堆Foo或Foo的子类。例如,您希望能够将String放入Stack<Object>。另一种方法不是这样,你不应该在Stack<String>

中插入另一个对象

在您的示例中,您声明了Stack<Integer>。您应该能够将Integers放在此堆栈中,而不是其他Numbers(如Double),如果您声明了参数<? super E>,则可以使用它。这就是为什么put-method应该有<? extends E>类型的参数。

答案 1 :(得分:1)

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

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

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

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

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

答案 2 :(得分:0)

pushAll方法中,您没有传递类型E,而是传递E的任何类型。因此,您可以传递Iterable扩展Number类型的任何Iterable,而不是传递Number Number个。{/ p>

原始示例使用Number类型,因为您可以传递Integer的子类的任何类型,例如BigDecimalInteger等等。

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

  1. java.lang.Number中
  2. java.lang.Object中
  3. 的java.io.Serializable
  4. 在您的示例中,您声明了泛型类型Stack<Integer>。让我们考虑以下几点。

    1. 您的堆栈永远无法容纳任何超级类型整数的项目
    2. 您的堆栈永远无法保存Integer的任何子类型的项目,因为Integer类是最终的,因此无法进行子类化。
    3. 因此,如果您要声明泛型类型Stack<Integer>,则您的iterable类型为Iterable<Integer>,因此您的Stack只能 保存Integer类型的项目。使用助记符 PECS 完全正确,但这只适用于选择具有至少一个超类型和至少一个子类型的具体类型的情况。