根据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
Integer
个Stack
并传递给Number
一个extends
。为什么不这样做?
为什么我总是要使用super
关键字?为什么使用super
是错误的?
当然,同样代表 comsumer 的观点。为什么消费者应始终为{{1}}?
PS:更具体地说,你可以在推荐书的“ Item 28 ”部分找到上面这个例子。
答案 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
的子类的任何类型,例如BigDecimal
,Integer
等等。
在您的示例中,您正在以相反的方式执行此操作。您正在使用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,因此允许以下三种类型:
在您的示例中,您声明了泛型类型Stack<Integer>
。让我们考虑以下几点。
因此,如果您要声明泛型类型Stack<Integer>
,则您的iterable类型为Iterable<Integer>
,因此您的Stack只能 保存Integer类型的项目。使用助记符 PECS 完全正确,但这只适用于选择具有至少一个超类型和至少一个子类型的具体类型的情况。