Software Development
Significant changes await the Java language: The impending August 2004 release
of J2SE 1.5—code-named “Tiger”—adds many new
key features to Java, including generics, enhanced for loops, autoboxing and
varags
, typesafe enum
, static import and metadata. Some of the new features
also provide built-in language support for common idioms and patterns. In short,
Sun’s goal is to make Java code “clearer, safer, shorter and easier
to develop—without sacrificing compatibility.”
However, many observers view the new features as “syntactic sugar”—nice but nonessential window dressings. An obvious, unstated goal is to one-up the competition, and in many Java Community Process discussions (posted at www.jcp.org) you’ll find debates concerning which prominent C# features should be included in Tiger (such as operator overloading and delegates).
Whatever your initial opinion, I’ll be addressing these changes in the coming months, giving you an inside look on how this tiger is shifting its stripes. Having worked on gobs of messy, bloated code, I’m enthusiastic about the changes, which I believe will help to simplify Java development and reduce the possibility of making coding errors. However, you should be warned that some of the new features can easily be abused, leading to different kinds of problems. Also, the generics feature adds a new layer of complexity that will be difficult for the average developer to master.
Generics
Collections in Java aren’t typesafe. You can add a String
to a collection
intended to hold Student
objects, and the compiler won’t complain. Also,
the collection is oblivious to the specific type of object it contains—you
must cast to the Student
type to send Student
messages to an object extracted
from the collection. If you add Strings
to the collection, you might get a ClassCastException
at runtime—and deciphering ClassCastExceptions
is a pain, since code
that adds to the collection may be dispersed and distant from the code that
extracts from the collection.
In Java 1.5, you may create parameterized collections by binding the collection to a specific type:
You store String
objects in the collection just as before, using the add
method.
If you try adding objects of any other type, the compiler will reject your code.
This is the biggest sell of generics—they offer an opportunity to
catch one more possible flaw at compile time instead of runtime.
The improvement in my mileage perhaps isn’t as great, since I now depend heavily on unit tests to catch defects. While a compiler can reduce the number of potential problems, it can’t ferret out all flaws. Extensive testing is still necessary, so I view the additional protections as nice but not essential.
When retrieving an element, you no longer need to cast:
I’m ecstatic about any elimination of casting, because it introduces the potential for runtime errors, it’s annoying and adds clutter to your code.
Multiple Type Parameters
To create a parameterized Map, you supply two type parameters: one for the key and one for the value.
When putting key-value pairs into the Map
, both the key and value must match
the corresponding parameter type. You don’t need to cast the return value
when extracting from the Map
.
Generics in the Class Library
Sun has retrofitted all collection classes and many other classes in the JDK
API to use generics. For example, you can bind the interface Comparable
to the
type being compared for sorting purposes. The compareTo
method then no longer
needs any casts:
Sun has retrofitted ThreadLocal
, Class
and other reflection classes with generics.
Creating Your Own
The following declaration of the NumList
class creates a parameterized type:
You follow the class name in the class declaration with the formal parameter
list, which contains the formal type name T. My use of the identifier T is arbitrary;
I could have chosen something like AnyNumericType
instead. The
compiler replaces any appearances in the class of the naked type declaration
T with the type to which instances of NumList
are bound.
So far, you use NumList
no differently than any parameterized collection type;
you create instances of NumList
by binding it to any type.
However, you want client code to be able to only bind NumList
to numeric wrapper types, so that you can provide a totaling mechanism. You
can specify a bounds on the formal type parameter T.
The extends Number
bounds clause means that you can bind NumList
to only Number
or a subclass of Number
. If you try to bind NumList
to, say, java.util.Date
,
your code won’t compile.
Since NumList
has as its upper bounds the type Number
,
you can call any valid Number
methods on objects of naked type
T. The code for the total method in NumList
demonstrates this:
How It Works
Sun uses a scheme known as erasure that translates generics code into
classic Java byte code. A declaration of the parameterized type List<Student>
is simply erased to List
. Java then replaces uses of naked types
with the upper bound, which is java.lang.Object
by default. It
also adds casts where necessary.
Versions of the pre-alpha compiler contained a javac switch -s
. This switch
let you view the Java source corresponding to the byte codes that would be generated
by compilation:
Note use of the -source 1.5
switch, necessary to compile with the new features
of J2SE 1.5.
The “erased” representation of NumList
:
Raw Types
Don’t fret—your existing Java code won’t break under the new compiler. Backward compatibility was a major consideration for J2SE 1.5. You can still use parameterized classes as raw types—without binding them to a specific type.
The upper bound still applies, however. If you try to add a non-Number
instance,
your code won’t compile.
When adding to raw collections, you will receive an “unchecked” warning. This means that your pre-1.5 code could potentially generate numerous warnings. Next month, I’ll cover ways to control those warnings, and delve into some of the advanced concepts that you must understand to master developing parameterized types.
Not Just Window Dressing
Trying It Out You can download an alpha version of Tiger from http://java.sun.com, and an early-access version from July 2003 includes, for the first time ever, the complete source code for the Java compiler itself. Remember, dealing with an alpha version means that you might encounter some defects. —J. Langr |
Other implementations of generics were proposed, but Sun chose erasure as the best route to complete backward compatibility. Stay tuned for more on this scheme’s implications—including its unfortunate limitations.
Next month: Advanced generics
Jeff Langr, owner of Langr Software Solutions, is the author of the book Essential Java Style and several articles, including two appearing in Software Development. He is currently working on a book on Java 1.5 and resides in Colorado Springs, Colo.