Java SE 6
Using Java Generics
Create more flexible classes
By: Steve Close
Nov. 3, 2003 12:00 AM
Have you heard? Generics will be in the next release of the Java SDK (code named Tiger, aka JDK 1.5). You might be wondering "What is a generic?" or "Why should I care?" or even "Cool! How do I use them?" This article will introduce generic coding, explain how generics are used and what their advantages are, and discuss how they will impact your work. To help you understand, I'll define generics and code a few examples to illuminate how to use them.
Generics are not a feature that everyone has used. A concept similar to generics was included in C++, i.e., templates. Although the syntax of Java generics is modeled after C++ templates, the Java syntax is easier to understand. In addition, the implementation of templates and generics are not the same. Java remains type safe and doesn't expose source code when supporting generics. In other words, Java adds the power of generic coding without many of the problems found in other language implementations.
What Are "Generics"?
To start learning how to use generics, we'll look at some collectionsbased examples and see what the advantage is.
In comparison, when using generics your code would look a little different (see Listing 2). Instead of creating an ArrayList reference, create an ArrayList reference to an ArrayList that can hold only strings (lines 9 and 10, Listing 2). The compiler will only allow strings to be added to this ArrayList, so any attempt to add objects of any type that is not a string will generate a compiler error. In addition, when you retrieve any value from the ArrayList, it's returned as a string so no cast is needed (line 14, Listing 2).
The collections you're familiar with in JDK 1.4 have been rewritten in 1.5 to handle generics. The good news is you won't have to fix or convert your existing code. Existing code will work with no changes in most cases. In fact you can still use the old form of all your favorite collections, but I'm not sure why you would, unless you're stuck on pre-1.5 Java.
Why Do We Need Generics?
The following code demonstrates how a method can expect a collection of Integers, not just a collection. The author of this method doesn't need to check type or cast, and the code is cleaner because of this. This static method filters all values larger than a given cap out of any given collection of Integers.
public static void capList
Notice there's no need for the author of this method to cast Object to Integer; the compiler will take care of it. The author of the code defines what type of collection can be used. If a user of this method attempts to pass the wrong type of object, it will be refused by the compiler, giving a "cannot be applied" compiler error. The code below, written to invoke the "capList()" method, contains the compiler error.
LinkedList list3 = new LinkedList();
Rewriting the same method without a generic collection would require you to cast each element retrieved from the collection. This in itself is not terrible (heck, we've been doing this in Java for quite a while), but the generic example is cleaner. The version that includes generics is cleaner and the methods declaration indicates to the user of the method what is expected as a parameter. The code itself, rather than documentation, leaves little question regarding what the collection can contain.
For comparison, the same method is included below without the addition of generics. Notice the cast in the if-statement and the use of instanceof to check the type before using it as an Integer.
public static void capList
Most of the generics changes that your average Java programmer will see are in the collections API and in extending those collections. The collections become easier to use and the compiler supports typing, as we saw above. Gone are the days when you created your own collection type to hold only one type of object; generics makes that code unnecessary.
The way generics change collections is not the end of the story. Code designers will be using generics to make code more flexible in more than just collections. If the generics change had only changed collections, Java would have been better off adding a dynamic array type.
You'll also be able to work with generics in your own code. The syntax is new to non-C++ converts, but it's simple to work with. As an example we'll create a class that has a parameterized (generic) property, a property that can be of any type. The class in Listing 3 is a standard JavaBean with a getter and setter, but the author of this class doesn't need to know what type is being stored. The property type is generic, so the author has inserted a < > after the class name. In this < > notation is a variable type name; it needs to be a valid Java identifier or a comma-separated list of valid identifiers. After assigning a Java identifier between the < >'s, you can use that type name in your class the same as any other type name. The actual type that it will be is defined by the user of the code in Listing 3.
When the client creates the bean, he or she will instruct it to hold some object type. From that point on the compiler will make checks for type safety; for example, when one is created with the following statement:
GenericBean<String> bean = new GenericBean;
After the reference to the GenericBean is defined as holding strings, the compiler will allow only strings to be sent into the setter, and the getX() method will always return a string. This flexibility and type safety adds up to easier code to write and to read.
In a recent JavaOne technical talk, Gilad Bracha (lead of JSR 14) predicted that one out of every million lines of code will have compiler errors. This prediction was based on the two errors found when compiling the 2+ million lines of code in the JDK. For my business 1 in a 1,000,000 seems pretty trivial, but I don't have 5,000,000 lines of mission-critical legacy Java to support.
The JCP expert group for generics worked hard to minimize the impact the addition of generics can cause. Because compatibility was a design constraint, a few annoying features have been accepted into generics, including new type casting possibilities. Specifically, you can still get class cast exceptions when working with generics. This is possible because a reference to a type can be stored in a reference that doesn't appear to be parameterized. Whoa, that might sound a little better in code. The following example shows an ArrayList stored in an ArrayList reference. The new reference to the array list called list5 allows you to add non-strings to the ArrayList.
//This snippet will compile fine
This issue can't be fixed because it's needed to make the new collections backward compatible with any code previously written, but it can cause trouble. Another way to put it is "It's the better of two imperfect options." The compiler allows this change, even though it will most likely cause a ClassCastException. This situation will probably occur rarely, typically when you're changing existing code that's used by other parties, but the problem exists. The compiler will inform you that you might have an issue by sending a warning. If you compile with the -warnunchecked flag, it will provide details. The above snippet produced the following warnings.
warning: unchecked call to add(E) as a member
Another annoying feature is the limitation on types. Generic types don't support primitives. You still can't make a collection hold int, char, or any of the primitives. The following code would fail to compile.
//This doesn't work ArrayList<int> intArray = new ArrayList();
The annoyance here is an old one. Java separated out primitives from objects to improve performance and that's a good thing, but it also forces developers to learn about "wrapper" types. If you want to store primitive ints in a collection, put them into java.lang.Integer objects.
I mention this annoyance because I hope that JSR 201 will be included in JDK 1.5, which would allow you to ignore the wrapper classes with the automatic "boxing" of types. If you want to give it a try, the code for boxing in Java appears below. Notice that the ArrayList type allows you to add ints without making Integers. The Integer is created behind the scenes. Boxing is a feature of C# and VB.NET that makes me just a little jealous, but they don't have generics yet.
LinkedList list2 = new LinkedList();
Trying It Out
Finally, you'll need to register as a member of the Sun Developer Network. Joining is painless. After downloading the zip, unzip it to a folder. I downloaded it onto my Windows box and unzipped it to c:\java. The zip contains a folder named "adding_ generics-2_0-ea" that I'll call JSR14HOME when describing the commands needed to compile and run your generics examples. The generics download contains JSR 14 features (Generics) as well as JSR 201 features (enumeration, boxing, static imports, and the "for-each" enhancement). Sample code is included as well as the source code for many of the changes to existing collections. There is a .bat file to help compile in the scripts directory, but I wrote my own because it was untested and required adding environmental variables I would never use again.
When compiling you need to include a few extra arguments for javac. The following is the javac statement I used to compile. JSR14HOME refers to the location of your adding_generics-2_0-ea folder and the JDK141Home is the Java_Home directory for an install of the JDK version 1.4.1.
javac -J-Xbootclasspath/p:JSR14HOME\gjcrt.jar-bootclasspath JSR14HOME
The Java command contains similarly obscure arguments. The command I used in running the generic examples is:
java -Xbootclasspath/p:JSR14HOME\gjc-rt.jar - cp classes FileName
If you're developing on a Unix platform, a make file provided in the JSR 14 download should help you on your way. You can find it in the examples directory in JSR14HOME.
Generics also solve one of the biggest annoyances in Java code: the constant type-checking with instanceof followed by type-casting. The compiler will catch many of the errors that would have generated ClassCastExceptions at runtime. This will make code more readable and type safer.
The addition of generics is a significant change. Although Java has gone through many versions, none has added as much syntax change as generics, except maybe inner classes. Maybe it's time to move the major version up one and announce JDK 2.0?
Reader Feedback: Page 1 of 1
Latest Cloud Developer Stories
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
SYS-CON Featured Whitepapers
Most Read This Week