10. nov. 2004

Using and Programming Generics in J2SE 5.0

URL: Using and Programming Generics in J2SE 5.0 and
Full article


The Need for Generics



The motivation for adding generics to the Java programming language stems from the fact that a collection doesn't contain information about the element type, the need to keep track of what type of elements collections contain, and the need for casts all over the place. Using generics, a collection is no longer treated as a list of Object references, but you would be able to differentiate between a collection of references to Integers and collection of references to Bytes. A collection with a generic type has a type parameter that specifies the element type to be stored in the collection.

As an example, consider the following segment of code that creates a linked list and adds an element to the list:


LinkedList list = new LinkedList();
list.add(new Integer(1));
Integer num = (Integer) list.get(1);


As you can see, when an element is extracted from the list it must be cast. The casting is safe as it will be checked at runtime, but if you cast to a type that is different from, and not a supertype of, the extracted type then a runtime exception, ClassCastException will be thrown.




Using generic types, the previous segment of code can be written as follows:

LinkedList list = new LinkedList();
list.add(new Integer(1));
Integer num = list.get(1);




Here we say that LinkedList is a generic class that takes a type parameter, Integer in this case.


As you can see, you no longer need to cast to an Integer since the get() method would return a reference to an object of a specific type (Integer in this case). If you were to assign an extracted element to a different type, the error would be at compile-time instead of run-time. This early static checking increases the type safety of the Java language.


To reduce the clutter, the above example can be rewritten as follows...using autoboxing:

LinkedList list = new LinkedList();
list.add(1);
int num = list.get(1);


As a complete example, consider the following class, Ex1, which creates a collection of two Strings and one Integer, and then prints out the collection:

Ex1.java


import java.util.*;

public class Ex1 {

private void testCollection() {
List list = new ArrayList();
list.add(new String("Hello world!"));
list.add(new String("Good bye!"));
list.add(new Integer(95));
printCollection(list);
}

private void printCollection(Collection c) {
Iterator i = c.iterator();
while(i.hasNext()) {
String item = (String) i.next();
System.out.println("Item: "+item);
}
}

public static void main(String argv[]) {
Ex1 e = new Ex1();
e.testCollection();
}
}



Again, an explicit cast is required in the printCollection method. This class compiles fine, but throws a CLassCastException at runtime as it attempts to cast an Integer to a String:



Item: Hello world!
Item: Good bye!
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer
at Ex1.printCollection(Ex1.java:16)
at Ex1.testCollection(Ex1.java:10)
at Ex1.main(Ex1.java:23)


Using Generics



Using generics, the Ex1 class above can be written as follows:


Ex2.java

import java.util.*;

public class Ex2 {

private void testCollection() {
List<String> list = new ArrayList<String>();
list.add(new String("Hello world!"));
list.add(new String("Good bye!"));
list.add(new Integer(95));
printCollection(list);
}

private void printCollection(Collection c) {
Iterator<String> i = c.iterator();
while(i.hasNext()) {
System.out.println("Item: "+i.next());
}
}

public static void main(String argv[]) {
Ex2 e = new Ex2();
e.testCollection();
}
}



Now, if you try to compile this code, a compile-time error will be produced informing you that you cannot add an Integer to a collection of Strings. Therefore, generics enable more compile-time type checking and therefore mismatch errors are caught at compile-time rather than at run-time.
... See more in the full article ...

Ingen kommentarer: