[英]Java Generics (bounded wildcards)
根据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
of Integer
并传递给Stack
这个Number
。 为什么不这样做?
为什么我总是要使用extends
关键字? 为什么使用super
是错的?
当然,这同样代表了消费者的观点。 为什么消费者应该永远super
?
PS:更具体地说,你可以在推荐书的“ 第28项 ”部分找到上面的例子。
当你声明一个Stack<Foo>
你指的是一堆Foo,或Foo的子类。 例如,您希望能够将String
放入Stack<Object>
。 另一种方法不是这样,你不应该在Stack<String>
插入另一个Object。
在您的示例中,您声明Stack<Integer>
。 你应该能够将Integers放在这个堆栈中,而不是其他的数字(如Double),如果你声明了参数<? super E>
<? super E>
。 这就是为什么put-method应该有一个类型<? extends E>
的参数<? extends E>
<? extends E>
。
尝试在堆栈中存储任意数字是不可能的,因为数字可能是整数的其他东西。 所以你的例子没有多大意义。
当对象作为使用者时,您将使用super,即当对象的泛型类型的实例作为参数传递给对象的方法时。 例如:
Collections.sort(List<T>, Comparator<? super T>)
在此示例中,sort方法从集合中获取T实例,并将它们作为参数传递给compare(T o1, T o2)
。
将此与您的第一个示例进行对比,其中Iterable src
是生产者。 pushAll()
方法调用Iterable的一个方法来生成(即返回)T的实例。在这种情况下,iterable是一个生产者,因此使用? extends T
? extends T
在pushAll
方法,你是不是传递一个类型E
,但延伸的任何类型E
。 因此,而不是通过一个Iterable
的Number
S,你可以通过任何Iterable
扩展类型的Number
。
原始示例使用Number
类型,因为您可以传递任何类型为Number
的子类,如Integer
, BigDecimal
等。
在您的示例中,您正在以相反的方式执行此操作。 您正在使用Integer
声明您的Stack
。 因此, pushAll
只能接受由Integer
扩展的那些类。 您将无法使用Numbers
(或任何其他类,因为Integer
是最终类)。
首先要注意的是Integer扩展了Number,所以你不应该将Number对象推入Stack of Integers。 但是,第一个示例将使用Integers,Floats,BigDecimal和所有其他Number子类。
你的例子没有多大意义。 一个类似<? extends Number>
<? extends Number>
表示Number和每个类型都允许来自Number的inheits。 因此,您可以定义上边界和下边界,从数字类型向下到最具体的边界。 反过来说, <? super Number>
<? super Number>
表示允许Number及其任何超类。 由于Number扩展了Object并实现了Serializable,因此允许以下三种类型:
在您的示例中,您声明泛型类型Stack<Integer>
。 让我们考虑以下几点。
因此,如果要声明泛型类型Stack<Integer>
,则您的iterable的类型为Iterable<Integer>
,因此您的Stack 只能保存Integer类型的项。 你完全正确使用助记符PECS ,但这只有在你选择了至少有一个超类型和至少一个子类型的具体类型时才有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.