JDK 1.5 Preview: Addition of Generics Improves Type Checking

JDK 1.5 Preview: Addition of Generics Improves Type Checking

enerics is one of the hottest features of Sun Microsystems’s upcoming JDK 1.5 (Tiger) release. Programmers and researchers have been clamoring for a feature like this for years, and an impatient few have even implemented their own prototypes already. Generics enables the creation of parameterized classes and methods, allowing Java developers to create custom variations of their code for different types.

Unlike JDK 1.4, Tiger isn’t just a set of new standard libraries?it features actual changes to the language itself. Generics is the most profound of these changes. Long a part of advanced academic languages, this new feature is a welcome addition to Java.

This article introduces the new generics feature by examining Sun’s example pre-release implementation. It demonstrates how to use generics in a sample program, an extensible networked multimedia framework (NMF). Finally, it goes over using generics in the Collections classes.

What Is Generics?
Generics is very similar to C++ templates, both in syntax and semantics. However, like most things in Java that are similar to C++, it is simpler. Since generics has a clear syntax, the following quick example will go a long way in explaining it:

public class Mailbox {  private Thing thing;  // ...}

This is a fragment from the source code for NMF. The first thing you should notice is the “” following the class name. This is a type parameter, which says you can create variations of the Mailbox class by providing different types to take the place of Thing. This means that Mailbox is a parametric class, a class that takes one or more parameters.

Thing isn’t really a type or class?you haven’t created a file called that contains a class definition for Thing. Rather, you’ll have to supply something to take the place of Thing. Here’s how you instantiate the parametric class:

Mailbox myMailbox = new Mailbox();

Now Mailbox clearly is the full name for a Java type. It means “the Mailbox class, with Thing replaced by String.” You could also use Mailbox, which would be “the Mailbox class, with Thing replaced by URL.”

In NMF, a Mailbox is an object that lets you send and receive messages of different types. A Mailbox, for example, lets you send and receive URLs, while a Mailbox lets you send and receive Strings.

The nice thing about this is that you don’t have to create two different classes to send and receive two different kinds of messages. Instead, you can create a single class, parameterized by the type variable Thing. You simply program this class to send and receive Things. Instantiating the Mailbox class with various types replaces the Thing type variable with actual types such as String and URL.

What Value Does Generics Add?
You may think Java doesn’t really need a mechanism of this type. After all, without generics, you could just write Mailbox so that it sends and receives objects of type Object, and add in a little bit of casting to get the objects to by the type you need. But generics provides two useful features.

First and foremost, generics offers better compile-time type checking. In a sense, a cast is just a run-time type check. Since it happens at run time, it also could fail at run-time. With generics, the type casting is implicit in the instantiation you are using? Mailbox vs. Mailbox vs. Mailbox?and it’s done at compile-time. By using a particular instantiation, you are in essence saying this Object is really going to be a String, and the compiler will verify whether this is consistent with everything else going on in the program.

Secondly, generics provides convenience. Casting can be irritating?you can feel like you’re telling the compiler something that should be obvious. It also makes code harder to read, since it can turn a simple assignment (or parameter-pass) into a more complicated expression. With generics, casting just goes away. This might sound like a small thing, but try generics for a while and then go back to casting?you’ll miss generics.

The NMF Example
NMF is, as previously mentioned, a simple, extensible networked multimedia framework. Put another way, it’s a glorified chat with graphics. The NMF client contains two sub-clients: a chat window and a shared whiteboard window. The chat window is what you would expect: type something in the text field and this sub-client sends the message to all users. The shared whiteboard is like the chat, but for drawing: draw some lines and this sub-client sends the drawing to all users. Figure 1 shows these windows in the NMF client.

Figure 1. The NMF Client: This NMF client has a chat window (on the left) and a shared whiteboard (on the right).

The nice thing about NMF is that it is extensible. If you want to create more sub-clients, you can. The application Figure 1 shows is only one way of setting things up. You can easily create an interface with more clients, arranged any way you want.

To run the program, you’ll need to run the server like so:

% java Server 5000

The 5000 is the port number on which you want the server to listen. You can change this value, but you’ll have to change the port number in sample.html as well. Sample.html is the client-side applet file. You can run the client by visiting this HTML page in your browser, or by running appletviewer like this:

% appletviewer sample.html

Either way, you should see a window like the one in Figure 1.

Now that you have the program running, the following sections describe how it works and how it uses generics.

The Client Class
The Client object establishes communication with the server. The sample application, which has a chat and a whiteboard, creates a single Client object to share between the chat and the whiteboard. If you had more sub-clients, then they would share the Client object as well.

The construction for Client takes a hostname and port:

Client client = new Client( hostname, port );

Once you have an instance of Client, you can pass it to each of the sub-clients.

The Mailbox Class
The central class in this program is Mailbox, which provides a convenient way to exchange objects with the server. Each sub-client should have its own Mailbox. But Mailbox is a parameterized class?you need to specify what kind of object each Mailbox is supposed to send and receive. For example, the chat sub-client sends and receives strings, so it creates its Mailbox like this:

Mailbox mailbox = new Mailbox( client, "chat" );

The first parameter to the constructor for Mailbox is the Client object, which lets the Mailbox talk to the server. The second parameter defines the channel that this Mailbox will use. Each sub-client uses a different channel. That way, you make sure that chat messages go only to other chat sub-clients and not to, say, audio sub-clients.

The Chat Mailbox
Using Mailbox is quite simple. Here’s how you send a text string:

mailbox.send( "hello chat server" );

Because this Mailbox is parameterized on the String type, its send() method takes a String as an argument. Likewise for the receive() method:

String message = mailbox.receive();

Take a look at the file included in the source code download. You can see how simply the chat sub-client is constructed using the Mailbox class.

The Whiteboard Mailbox
The whiteboard is a bit more complicated. It lets you draw lines into a window and these lines show up on everyone else’s screens. So you need to send lines across the network.To handle this, you create a class called Line, which contains the (x ,y) positions of a line’s endpoints. To send and receive such objects, you create an appropriate Mailbox:

Mailbox mailbox = new Mailbox( client, "whiteboard" );

Here, you’ve used the Line class instead of the String class. It doesn’t matter that Line is a class that you created and that String is built into Java?any class will do.

Generics Code Meets Non-generics Code
Internally, Mailbox doesn’t do all that much. When an object is sent, the Mailbox puts the object on the Client‘s send queue, and when an object comes in to the Client, the Client puts it on the Mailbox‘s receive queue.

The interaction between the two is interesting, however. Mailbox is a parameterized type, which means that each separate instance deals with a different message type. Client, on the other hand, deals only with Object types. In this sense, Client is written in the old, pre-generics style. Code written using generics inevitably will have to interact with code not written with generics. Such instances will require a cast.

In the case of NMF, the cast happens when objects are coming from the server. The Client determines to which Mailbox the object should go. It treats the Mailbox as an Enqueuer, which is an interface defined like this:

public interface Enqueuer{  public void enqueue( Object object );}

The Client passes the Object that it has received to the Mailbox via this method. Here’s Mailbox‘s implementation of enqueue():

public void enqueue( Object object ) {  Thing thing = (Thing)object;  readQueue.put( thing );}

Here’s where you’ve hidden the inevitable cast. Client deals with Objects, while Mailbox deals with Things, and here is where the two meet.

In actuality, an interface isn’t required here?it’s just a nice way of structuring things. But the important thing is to recognize that generics-based code and non-generics-based code have something of an impedance?the generics code does away with run-time type checks, while the old-style code still needs them. When objects are passed from old-style code to new-style code, it generally means a typecast will be produced?and thus the possibility of a type exception.

Generics and Collections Classes
The NMF program actually uses generics in several other places, but not by defining new parameterized classes. Rather, it uses existing parameterized classes from the Java Collections classes.

The Collections classes in the java.util package are used to create various kinds of object collections (lists, sets, maps, and so on). They also provide useful iteration classes. However, they can be a bit annoying. During your Java programming, you’ve probably typed something this more times than you’d like to remember:

for (Enumeration e = vector.elements(); e.hasMoreElements();) {  String s = (String)e.nextElement();}Or, using the newer classes, something like this:for (Iterator it = list.iterator(); it.hasNext();) {  String s = (String);}

It’s a lot to type over and over. Generics can get rid of that annoying cast. All of the Collections classes have been turned into parameterized classes, generally in the obvious way. For example, a List of Strings can be created like this:

List myStrings = new ArrayList();

And you can use it like this:

for (Iterator it = list.iterator(); it.hasNext();) {  String s =;}

See? The cast is gone. Of course, you had to add that in there, so you haven’t actually saved any typing in this case. But you would if you used the variable it more than once inside the loop.

The real win will come when the full JDK 1.5 feature set is implemented. It will contain a new for syntax that will make iteration very concise:

for (String s : list) {  // ...}

It seems that Java’s designers have finally realized the centrality of iteration, and have made it as concise as possible. As noted language hacker Paul Graham has said, “succinctness is power.” And that for loop is about as concise as you can get.

Using Collections Classes
This section looks at a few places where the NMF program uses the Collections classes.

As you’ll recall, the Client receives messages from the server and hands them to the appropriate Mailbox, based on the channel on which the message was sent. Client carries out this mapping using a java.util.Map object. More precisely, a Map that maps channels (Strings) onto Mailboxes, where the Mailbox is represented as an Enqueuer:

private Map mailboxes = new HashMap();

Using the Map is now cast-free:

Enqueuer mailbox = mailboxes.get( channel );

Parameterized collections are used in a few other places in the code:

  • The WhiteBoard class uses a Set to store the set of lines that has been drawn.
  • The Server class uses a List to store objects that respond to Client requests.
  • The Queue class (not part of collections) is a simple blocking queue, parameterized by the type of the object it contains. This class is used as the read queue in Mailbox and the write queue in Client.

Finally, Generics in the JDK
Generics is a well-understood language formation that has not caught on particularly well in commercial languages, but it has a strong theoretical underpinning and a solid prototype implementation that is available now.

Generics does more than simply eliminate extra casts. It provides extra typing information to the compiler, allowing it to perform certain typing checks at compile-time that previously had to be done at run-time. In combination with the Collections classes and the new for loop syntax, generics can be very powerful. Watch for it in the upcoming release of JDK 1.5?finally.


Share the Post: