Harald Kirsch

… ist auch im Web zu Hause.

2008-12-06 (Saturday)

Java Generics Misunderstanding

Today I write this down to help me understand one of the trickier things about Java generics. I hope I get it right. What I always struggle with is the meaning of generic type bounds, like

Set<A extends Wine> cellarA;

What exactly is the difference between this and the much simpler

Set<Wine> cellarB;

My first reaction always is: cellarA may contain all kinds, i.e. subtypes, of Wine. But wait, if we have a type hierarchy Wine > RedWine > Bordeaux, there is nothing wrong with putting a Bordeaux into cellarB like in

cellarB.add(new Bordeaux());

so certainly cellarB can contain all kinds of Wine. The sligthly confusing insight is, that it is just the other way around. It is cellarA that, once the A is instantiated, is more restrictive in the types it may contain. Suppose A is instantiated in a certain context with Bordeaux. Then, in fact, cellarA effectively has the type

Set<Bordeaux> cellarA;

and we realize that now it may not contain any kind of Wine but more restrictively only any kind of Bordeaux. A more complete example which compiles and shows the difference:

public abstract class TTT {
   
  private static class Wine {}
  private static class RedWine extends Wine {}
  private static class Bordeaux extends RedWine {}
  
  private static class Cellar<A extends Wine> {
    private A bottle;
    public void shelve(A a) { bottle=a;}
    public A retrieve() {return bottle;}
  }
  public static class WineStore {
    private Wine bottle;
    public void shelve(Wine a) { bottle=a;}
    public Wine retrieve() {return bottle;}
  }
  static {
    Cellar<Bordeaux> cellar = new Cellar<Bordeaux>();
    // cellar.shelve(new Wine()); ** not allowed
    cellar.shelve(new Bordeaux());
    Bordeaux bordeaux = cellar.retrieve();
    System.out.println(bordeaux.getClass().getName());
    
    WineStore store = new WineStore();
    store.shelve(new Wine());
    Wine bottle = store.retrieve();
    System.out.println(bottle.getClass().getName());

    store.shelve(new Bordeaux());
    bottle = store.retrieve();
    System.out.println(bottle.getClass().getName());
  }
}