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 example | Same 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:
- Trying to add something of the wrong type gives a compile-timer error.
- Getting elements doesn't require an explicit downcast, altho this example doesn't show how often this is convenient.
- Specifying the type provides a lot of documentation.
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
Notation | Meaning |
---|---|
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.
- You can't create objects of type T.
- You can't create arrays of type T, altho you can create new Collections data structures.
- Don't use them for statics.
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
- Angelika Lager's academic Java Generics FAQ can be found at www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html .
-
Not an introduction, but perhaps useful. (Talking Tiger, generically speaking) has explanations of some of the generics features.
-
Sun has a tutorial on generics at java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf. It's not a great article, but you might find something useful here.
A discussion of generics in Java, C++, and C# is at www.artima.com/intv/generics.html