LiTTle
LiTTle

Reputation: 1881

Java Generics (bounded wildcards)

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 Integers 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

Answers (5)

My-Name-Is
My-Name-Is

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:

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

In your example you declare the generic type Stack<Integer>. Let's consider the following.

  1. Your Stack is never be able to hold items of any super type of Integer
  2. Your Stack is never be able to hold items of any subtype of Integer, since Integer class is final and thus it can't be subclassed.

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

Martin
Martin

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

JB Nizet
JB Nizet

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

ipavlic
ipavlic

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 Numbers, 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

Aleksander Blomsk&#248;ld
Aleksander Blomsk&#248;ld

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

Related Questions