Reputation: 1881
According to the book "Effective Java" of Joshua Bloch there is a rule about how/when use the bounded wildcards in generics. This rule is PECS (Producer-Extends, Comsumer-Super). When I study the following example:
Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = ... ;
numberStack.pushAll(integers);
I understand that this rule fits perfect in this example. I have to declare the method pushAll
as the following sample:
// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
{
push(e);
}
}
But what happens if I have the following example?
Stack<Integer> integerStack = new Stack<Integer>();
Iterable<Number> numbers = ... ;
integerStack.pushAll(numbers);
I have to declare the pushAll
as it follows:
public void pushAll(Iterable<? super E> src) {
for (E e : src)
{
push(e);
}
}
According to PECS rule the above declaration is wrong. But I want to have a Stack
of Integer
s and pass to this Stack
a Number
. Why not to do it?
Why should I always use the extends
keyword? Why using super
is wrong?
Of course the same stands for the comsumer's point of view. Why a consumer should always be super
?
PS: To be more specific you can find this above example at the sector "Item 28" of the referred book.
Upvotes: 1
Views: 1383
Reputation: 4940
Your example doesn't make much sense. A construct like <? extends Number>
means that Number and every type is allowed which inheits from Number. So you define an upper and a lower boundary, from type Number down to the most specific one. The other way round, <? super Number>
means that Number and any of its supertyes are allowed. Since Number extends Object and implements Serializable the following three types are allowed:
In your example you declare the generic type Stack<Integer>
. Let's consider the following.
So, if you want to declare the generic type Stack<Integer>
, your iterable is of type Iterable<Integer>
and thus your Stack can only hold items of type Integer. You are totally right with the mnemonic PECS, but this only works if you have choosen a concrete type which has at least one super type and at least one subtype.
Upvotes: 0
Reputation: 3058
First thing to notice is that Integer extends Number, so you shouldn't be pushing Number objects into a Stack of Integers. However, the first sample will work with Integers, Floats, BigDecimal and all other Number subclasses.
Upvotes: 0
Reputation: 692023
Trying to store arbitrary numbers in a Stack can't possibly work, since a Number could be something other that an Integer. So your example doesn't make much sense.
You would use super when the object asts as a consumer, i.e. when instances of the generic type of the object are passed as arguments to methods of the object. For example:
Collections.sort(List<T>, Comparator<? super T>)
In this example, the sort method takes T instances from the collection, and passes them as argument to the compare(T o1, T o2)
of the comparator.
Contrast this to your first example, where the Iterable src
is a producer. The pushAll()
method calls a method of the Iterable which roduces (i.e. returns) instances of T. In this case, the iterable is a producer, hence the use of ? extends T
Upvotes: 1
Reputation: 4966
In the pushAll
method, you are not passing type E
, but any type that extends E
. So, instead of passing an Iterable
of Number
s, you can pass any Iterable
of a type that extends Number
.
The original example uses a Number
type because you can then pass any type that is a subclass of Number
, like Integer
, BigDecimal
and so on.
In your example, you are doing it the other way around. You are using Integer
to declare your Stack
. Therefore, pushAll
will only be able to accept those classes that are extended by Integer
. You will not be able to use Numbers
(or any other class, because Integer
is a final class).
Upvotes: 0
Reputation: 18552
When you declare a Stack<Foo>
you mean a Stack of Foos, or subclasses of Foo. As an example, you would expect to be able to put a String
in a Stack<Object>
. The other way is not true, you should not be able to insert another Object, in a Stack<String>
.
In your example you declare a Stack<Integer>
. You should be able to put Integers in this stack, but not other Numbers (like a Double), which you would if you declared the parameter <? super E>
. That's why the put-method should have a paramter of the type <? extends E>
.
Upvotes: 3