Motivation of Generics
a) Code Reuse
public static void printArray( Integer[] inputArray ){
for ( Integer element : inputArray )
System.out.printf( "%s ", element );
}
public static void printArray( Double[] inputArray ){
for ( Double element : inputArray )
System.out.printf( "%s ", element );
}
public static void printArray( Character[] inputArray ){
for ( Character element : inputArray )
System.out.printf( "%s ", element );
}
public static <T> void printArray( T[] inputArray )Method printArray ‘s type parameter section declares type parameter T, as the placeholder for the array element type that printArray will process. T is replaced in the parameter list as the array element type as well it will be replaced in the for statement. These are the exact same locations where the overloaded printArray methods made use of Integer, Double, Character as the array element type.
{
// display array elements
for (T element : inputArray)
System.out.print(element + " ");
System.out.println();
} // end method printArray
Before Java 2SE 5.0 this kind of generic programming was achieved by means of Object references. For example a generic list was implemented as a collection of Object references. Since Object is the superclass of all classes the list of Object references can hold references to any type of Object. Elements were added to the collection by passing an element reference. Each time it was extracted an object from a collection it was received an Object reference. Before the retrieved element could be effectively used, we were suposed to restore the element’s type information. For this purpose it was required to cast the returned Object reference down to the elements type. Here is an example:
LinkedList list = new LinkedList();It was required to cast the Object reference returned from method get()and promise the compiler that we were going to assign an Integer reference to i . The cast is safe because it is checked at runtime. If we tried a cast to a type different from the extracted element’s actual type, a ClassCastException would be raised, like in the example below:
list.add(new Integer(0));
Integer i = (Integer) list.get(0);
// fine at compile-time, but fails at runtime with a ClassCastExceptionNow, this latter approach is not that effective because we cannot identify possible bugs until runtime (When the application crashes or in best case throws an error). Additionally, the lack of information about the collection’s element type required countless casts in all places where elements are extracted from a collection, making our code harder to read and unsafe. With generics, information about the type elements a collection might contain is provided to the compiler. Instead of treating every collection as a collection of Object references, we will distinguish between collections of references to integers and collections of references to Strings and so on. A collection type would be a parameterized (or generic) type that has a type parameter, which would specify the element type. With a generic list, the previous example would look like this:
String s = (String) list.get(0);
LinkedList <Integer> list = new LinkedList <Integer> ();With this approach the get()method of a generic list returns a reference to an object of a specified type, in this case Integer, that means the cast from Object to Integer is not required because the compiler knows the method will return an Integer from the list. As well, if the extracted element was of a different type would now be caught at compile time already, rather than at runtime. The example below would simply not compile:
list.add(new Integer(0));
Integer i = list.get(0);
String s = list.get(0); // compile-time errorThis way, Java Generics provide programmers with a powerful capability for software reuse with compile-time type safety.