Java Notes: Generics

Using predefined generic classes is commmon, writing them is less common. Generics are primarily a way for library authors to write something once, which users can customize to their own types. You generally won't define generic classes and methods yourself, but you do have to understand how and why they are used.

Basic problem -- restricted types or completely open

When you want to write something that works well with objects from many classes or interfaces as parameters, you have a problem. When you choose a type T, then only objects of that type or subclass can be used. This is ok, but if you want something general, you often have to go all the way up the inheritance hierarchy to Object, which works for all types, so it is completely generalized. This is how the Java Collections were written before Java 5 - they often used Object as the type of their parameters. This was very convenient for the implementors, but less useful to the users.

Restricting the type. For most data structures, there is only one type that really should be used, for example, you have an ArrayList of Strings, or an ArrayList of Dates, but you rarely mix them. Indeed it is often considered very bad style to mix them. However, library methods that work with Object allow the use to mistakenly add different types.

Static vs Dynamic typing

Static/strong typing. One of the attractions of Java is that it has what is known as strong typing -- the type of variables (and other elements) is declared and values assigned to the variable must but that type. This causes more busy work when writing the program to get the types right, but the error messages that the compiler produces are much better than allowing code that might assign incorrect types at runtime.

Dynamic/weak typing is used in some languages (eg, Ruby), which allow different type values to be assigned to variables, which then have the type of the last assigned variables. Proponents of this approach say that not having to worry about getting types correctly specified in the source code makes coding faster, and with TDD (Test-Driven Development) any bad assignments are quickly discovered and fixed.

Java uses static/strong typing and the introduction of generics allows even stronger typing.

Example comparing the legacy with generic styles

Legacy non-generic exampleSame example using generics
// Typical Collections usage before Java 5
List greetings = new ArrayList();
greetings.add("We come in peace."); 
greetings.add("Take me to your leader.");
greetings.add("Resistance is futile.");

Iterator it = greetings.iterator();
while (it.hasNext()) {
    String aGreeting = (String)it.next();
    attemptCommunication(aGreeting);
}
// Same example using generics.
List<String> greetings = new ArrayList<String>();
greetings.add("We come in peace."); 
greetings.add("Take me to your leader.");
greetings.add("Resistance is futile.");

Iterator<String> it = greetings.iterator();
while (it.hasNext()) {
    String aGreeting = it.next();  // No downcast.
    attemptCommunication(aGreeting);
}

Specifying the type of element in the collection has nice consequences:

Type parameter naming - T, U, ...

Altho any names are possible for type parameters, they are traditionally written in upper case and often taken from the sequence T, U, V, ... Other upper case letters are used when they have specific meanings, eg, K for the type of a key, E for element type.

Reading type parameters

NotationMeaning
List<T>List of elements of type T (T is a concrete type)
List<?>List of any type (? is an unbounded wildcard type)
List<? super T>List of any type (? is a bounded wildcard supertype of T)
List<? extends T>List of any type (? is a bounded wildcard subtype of T)
List<U extends T>List of any type (U must be a supertype of T)

Where you can't use generic types in defining a generic class/method

Generic types, altho they have solved some problems, also have some limitations, not all of which are entirely obvious.

Java's type erasure implementation of generic types

To maintain compatability with JVM (Java Virtual Machine) implementations, Java's generics are only seen by the compiler. At execution time all generic type information has been "erased" and replaced by Object. This is a clever way to achieve compatibility, but it also causes a few of the complications, eg, forbidding arrays of generic types.

Links to resources on new Collections features