Java Generics and Collections

July 3, 2008

I hate these situations in coding where common sense fails you. You are supposed to know anything and everything of the language.

Suppose we have an interface Bounceable, and Baloon and GolfBall are two classes that implement the bounce() method of the interface.

public interface Bounceable {
     void bounce();
}
public class Baloon  implements Bounceable {
     public void bounce() {
          System.out.println("I am a Baloon");
     }
}
public class GolfBall implements Bounceable {
     public void bounce() {
          System.out.println("I am a GolfBall");
     }
}

We have a class Person, which takes a list of Bounceable objects and plays them.

public class Person {
     public void playAll(List<Bounceable> bList) {
          for(Bounceable b : bList) {
               System.out.println("Bounceable object: " + b);
          }
     }
}

Now, from a main method, lets try to test something. First, lets create a list of Bounceable objects and pass it to the playAll() method of the Person object.

List<Bounceable> bList = new ArrayList<Bounceable>();
bList.add(new Baloon());
bList.add(new SoccerBall());
bList.add(new GolfBall());
Person person = new Person();
person.playAll(bList);

This works great. The playAll() method is expecting

List<Bounceable>

and we just passed bList to it. Perfect!

Since Bounceable is an interface, now we would also hope that it would accept List of any subclasses of Bounceable. At least, thats what I have known of polymorphism in Java. And it works that way with Arrays. However, thats not the case with Generics and Collections.

Baloon implements Bounceable. So lets try to create a

List<Baloon>

and pass it to the

playAll()

method. But this simply doesn’t work.

List<Baloon> baloonList = new ArrayList<Baloon>();
baloonList.add(new Baloon());
baloonList.add(new Baloon());
baloonList.add(new Baloon());
person.playAll(baloonList);

This is because once we are in the

playAll(List<Bounceable> bList)

method, the list is treated as

List<Bounceable>

and we should be able to add any objects of type Bounceable.

// See, we just corrupted the baloon list with a golf ball.
baloonList.add(new GolfBall()); 

Put simply, if a method foo() takes an argument

List<x>

, you cannot pass a

List<y extends X>

, because the compiler is scared that you will take the

List<x>

and put stuffs into it that aren’t of type Y, thus corrupting the entire list type.

But there is a way that you are allowed to pass

List<Baloon>

it to the method, but you cannot modify the list. For that we have to change our method signature to:

playAll(List<? extends Bounceable> bList)

? extends X means any unspecified type of X. So we can pass

List<Baloon>

to the method with signature

List<? extends Bounceable>

and retrieve our Baloon objects back in the playAll() method, because the compiler knows that we aren’t allowed to add anything to the Baloon List.

Sigh!!!!! Interesting, isn’t it? While Generics is great feature for type safety, the way this feature is inconsistent with other features within java makes it very difficult for beginners and students to learn and feel confident about the language. With Generics, Java just turned into big and too confusing of a language.

1 Comment on Java Generics and Collections

Respond | Trackback

  1. Justin says:

    There is nothing complex about generics. It is a patch to language that is crippled by static typing. But there is not anything difficult about it. Closures are something that is good for the Java language. You think anonymous inner classes are less confusing? Ruby and Python both have closures (though Ruby does not implement this perfectly). Closures are something the Java language should have had years ago.

Respond

Comments

Comments