Understanding Java5 Generics

Good article on Java5 Generics by Martin Wolf:

http://blogs.infosupport.com/martinw/articles/generics.aspx

It seems many programmers are confused about generics, in particular the use of the ? extends ... notation. The question mark is called a type wildcard, and is typically used as the value of a type parameter in a generic method. It means that wherever the method is invoked in your code, the compiler infers a specific type to be substituted for the wildcard and enforces that at compile time. The notation ? extends X is a bounded wildcard, meaning that the deduced type must be a subtype of X. Here’s a variation on Wolf’s example which I think might help clarify the difference.

Say you have a class Animal which supports the method feed(), with subclasses Cat and Dog. Consider this code which does not use generics:

public static void feedAnimals1(List<Animal> pets) {

	for (Animal a : pets) {
		a.feed();
	}

	for (int i=0; i < pets.size(); i++) {
		pets.set(i, (i % 2 == 0) ? new Cat() : new Dog());
	}
}

The argument pets is of type List<Animal>. That means that each element can be a Cat or a Dog object. They all understand the feed() method, and any element can be reassigned to a Cat or Dog object (since the elements are of type Animal).

But what if we write it this way instead?

public static void feedAnimals2(List<? extends Animal> pets) {

	for (Animal a : pets) {
		a.feed();
	}

	for (int i=0; i < pets.size(); i++) {
		pets.set(i, (i % 2 == 0) ? new Cat() : new Dog());    // compile error
	}
}

This second version uses generics. In this case we are not saying that the input must be of type List<Animal>. Rather, we are saying that the input must be of type List<X> where X is any subtype of Animal. At each location in the code where this method is called, the Java compiler will guess a suitable type for X at compile time and enforce it. It’s exactly as though you had written different overloaded versions of this method, one for lists of Cat objects, one for lists of Dog objects, and so on.

In the generic example the compiler will not let you randomly assign dogs and cats to the list elements, because the caller might legitimately pass in a list of Dog objects in which case the cat assignment would be illegal, and vice versa. But apart from that restriction, the generic version is more flexible. It can work on inputs of type List<Animal>, List<Dog>, or List<Cat> e.g.

	List<Dog> pets = new ArrayList<Dog>();
	pets.add(new Dog());
	pets.add(new Dog());
	feedAnimals1 (pets);    // compile error - pets must be of type List<Animal>
	feedAnimals2 (pets);    // ok

The Sun tutorial on generics explains all in detail:

http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *