As with any new feature in any product worth talking about, some attention needs to be devoted to backward compatibility. In fact, in the case of generics, guaranteeing full backward compatibility played a major role in the changes to the Java Language and Java Compiler Specifications. (Hint: We have not mentioned anything about the changes to the Java Virtual Machine Specification!)
The first things you will notice with respect to backward compatibility is the ability to create Collection instances as you have done without genericity (see "Raw Types" later in this article). This means the code from Listing 1 will compile and run. However, the compiler will generate warnings that could not check the type of the instance being added to the Collection. The following shows the compiler warning that was generated when Listing 1 was compiled:
ArticleCode.java:21: warning: unchecked call to add(E) as a member of the raw type java.util.ArrayList list.add(new String("test string")); ^
How it Works: Type Erasure
To make code written for Java 1.5 SDK fully compatible with the Java 1.4 JVM, a process called type erasure is used to map the new syntax to the current JVM specification.
Genericity is implemented in the 1.5 release through Java language changes and a process known as Type Erasure. Type erasure is the process of translating or rewriting code that uses genericity into non-generic code. Listing 6 shows the code from Listing 2 decompiled (using Jad).
Listing 6 Decompiled collections example using genericity
1 protected void collectionsExample(){ 2 ArrayList arraylist = new ArrayList(); 3 arraylist.add(new String("test string")); 4 inspectCollection(arraylist); 5 } 6 7 protected void inspectCollection(Collection collection){ 8 String s; 9 for(Iterator iterator = collection.iterator(); iterator.hasNext();) 10 s = (String)iterator.next(); 11 12 }
Notice that the decompiled code does not have any dependency on the new language features that enable genericity. In fact, the decompiled code looks almost identical to the code that we would have written without genericity (see Listing 1).
Formally speaking, the erasure of a parameterized type is that type with each of its type parameters replaced by the upper bound of possible types. For example, the erasure of String is String, the erasure of <T> is Object, and the erasure of <T extends Number> is Number.
This means that for the most part, code can be written using generics, compiled, and distributed to clients that are running the Java 1.4 SDK. This is possible because the JVM specification has not changed!