Wednesday, 4 May 2011

Using Generic bounded wild-cards in an API

Yesterday’s blog touched on the strange aspect of bounded wild-card generics showing that you can create typed collections into which you can’t add anything except null. The example, covering Fruit classes, begs the question of why you can write ? extends Fruit.

The real reason that you can write ? extends Fruit is so you can write your own generic classes. For example, consider the dummy stack implementation below that uses bounded wildcards to increase API flexibility.

  class DummyStack<T> {

   
private final List<T> stackElements = new ArrayList<T>();

   
public void pushAll(Collection<? extends T> collection) {

     
for (T item : collection) {
       
stackElements.add(item);
     
}
    }

   
public void popAll(Collection<? super T> collection) {

     
for (T item : stackElements) {
       
collection.add(item);
     
}
    }

   
// Other methods go here
 
}

This contrived class (JDK contains its own Stack) has two methods of interest: popAll(...) and pushAll(...). pushAll loads stuff onto the stack and uses ? extends E as we're adding to the stack anything that is an E or extends E. popAll converts the stack into a collection and uses ? super E as we're consuming anything from our stack that is an E or a super-class of E.

The code below shows how to use the fake stack class:

  public static void checkingFruit() {

   
List<Fruit> fruit = new ArrayList<Fruit>();
    fruit.add
(new Fruit());
    fruit.add
(new Apple());
    fruit.add
(new Orange());

    DummyStack<Fruit> handler =
new DummyStack<Fruit>();
    handler.pushAll
(fruit);
    handler.popAll
(fruit);
 
}

 
public static void checkingApples() {

   
List<Apple> apples = new ArrayList<Apple>();
    apples.add
(new Apple());

    DummyStack<Fruit> handler =
new DummyStack<Fruit>();
    handler.pushAll
(apples);   // allowed as Apple extends Fruit
    // handler.popAll(apples);   // not allowed...

   
DummyStack<Apple> handler2 = new DummyStack<Apple>();
    handler2.pushAll
(apples);
    handler2.popAll
(apples); // allowed in '? super T' as Fruit is super of
                 // Apple
 
}

The basic premise used to decide whether to use ? extends E or ? super E is this. If the other object is giving your object something (a source) then use ? extends E. If the other object is receiving stuff from your object (a destination) then use ? super E.

No comments: