Saturday 12 June 2010

Generic methods provide type inference

A feature of Java that is powerful yet under-used is type inference. Generic methods provide the ability to infer types at compile time; that is they take the generic typing information from the type that they are assigning to as opposed to the usual inference from a passed instance or explicit declaration.

Let me give you an example from the Collections class:

class Collections {
...

public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}
...

It can be seem that the method emptyList() has a generic type definition, and subsequently provides a generic typed cast of the raw EMPTY_LIST to a list of the generic type. This allows the following usage:

List<string> s = 
     Collections.emptyList();

It can clearly be seen that this construct has no cast, yet satisfies the compiler. So, what is going on? Well the type <T> is being ‘infered’ from the type of list that you are assigning the empty list to, therefore the (List<T> ) cast on the definition of emptyList is automatically providing the correct type for the assignment.

Use with caution! At runtime, type elision removes all type information from the generic definitions so we are effectively back to raw types for everything, and as this is an empty List that is being returned, is type safe. In reality, it is actually a special immutable null implementation of the List<T> interface, to prevent the unwitting developer adding typed items.

This technique can be used in the visitor pattern, to infer type information, and prevent the double dispatch issues.

Don't confuse with the formal type inference that is proposed for Java 7, where the following would be allowed syntax in isolation, and simple allows the lazy programmer to not bother to define the assignment type.


map := new HashMap<string, integer>();