What Generic Code Looks Like

There are three parts to generics:

  1. Declare the class which will have generic type parameters

  2. Declare/instantiate an object of that class, passing the actual type arguments to it.

  3. Invoke methods on the object of the instantiated generic class.

Using the instantiated generic class is no different to using a non-generic class, so we won't spend a lot of time looking at that. The class declaration has to come before any instantiations, so let's look at declarations first.

Declaring a class with generic type parameters

One of the collection classes in the Java API is a class that maintains a linked list data structure; it is class java.util.LinkedList. When you have some objects that you want to keep in a linked list, you create an instance of the LinkedList class. You can then call methods like add(), getFirst() on the instance to put your objects in a list, and retrieve them.

Up until JDK 1.4 LinkedList looked like this (simplified to omit extraneous detail):

public class LinkedList {
    boolean add(Object o) { /*more*/ }
    Object  getFirst()    { /*more*/ }
}

image

In JDK 1.5, LinkedList, along with many other classes in the API, was updated to accept a generic type parameter, and it now looks something like this:

public class LinkedList <Element> {
    boolean add(Element o)   { /*more*/ }
    Element getFirst()       { /*more*/ }
}

The key piece is the <Element> after the classname. The notation is similar to that used in Backus-Naur Form beloved of compiler-writers everywhere, though the chief designer told me this was not his inspiration. In BNF, a word in angle brackets <word> stands for something that will be explained, described, or filled out later, which is exactly the role of a generic parameter. The angle brackets say, “Here are generic type parameters”, and the identifiers are the names of the parameters. C++ uses angle brackets for type parameters, and the similarity to C++ was considered a plus (yikes!) But if the team could have found some unused punctuation that didn't need the shift key, they would have done so.

Once you have declared the name, you can use it nearly anywhere in the body of the generic where a type name is expected. It's as simple as that. You can provide any identifiers, “Element” is only an example. Sun recommends you use names that are one character long for type parameters, such as 'E' instead of “Element”.

In the sample code above, the argument to the add() method has been updated. It now takes an object of type Element (whatever that eventually turns out to be). And the getFirst() method now returns an Element, not an Object.

A couple of questions come to mind. How does this work if you want your linked list to contain arbitrary objects of different types, say, it's a list holding all the different objects in your program? If they have a common parent type, then you could parameterize the list with that type. Ultimately, you would need to set the list up to accept Objects. The more general the type that the list works on, the less and less the generic feature buys you.

Here is the code to create the three data structures mention in the previous box:

Table<String, Integer> phonebook  = new Table<String, Integer> ();

Table<Integer, Integer> partsTable = new Table<Integer,Integer>();

Table<Timestamp, String> appts = new Table<Timestamp,String>();

Generic parameters are intended for classes that do all their work on one specific type at a time, and where this work can be applied to many different types. There are several classic examples of this:

  • Container classes, which implement a data structure holding objects of type X.

  • Input/output, where you want to output a value in a formatted way, such as a byte, a short, an integer, and so on. Java I/O uses a completely different model (Gulp! Actually it has several different models as successive designers have tried to plug past gaps. JDK 1.4 had “new I/O” so the JDK 1.5 improvements are “even newer new I/O”). Java I/O wasn't changed to use generics.

  • Sorting and Searching classes. These utilities are part of the collection classes, and so have been genericized.

  • Cloning. The clone() method in Object did not need to be updated to use generics. Instead, when we override it, we can use the covariant return type feature to make it return an object of precisely the correct type (see page 238).

That's the basics of declaring a generic type; next we'll walk through showing how to pass type arguments to a generic.

Instantiating an object and passing arguments to the generic type

After you have set up your parameterized class, you need a way to create an instance of it and at the same time pass the actual type arguments. As usual, you create instances with constructors. You pass the type arguments in angle brackets, right before the parentheses for the constructor arguments. So you've got both sets of arguments appearing: type arguments in angle brackets, and constructor variable arguments in parentheses. Here's an example:

LinkedList<String> familyAddresses = new LinkedList<String> ( );

As always, you can use a super class on the left, and instantiate a more specific subtype:

Collection <String> familyAddresses = new LinkedList<String> ( );

Doing that keeps your options open for changing the underlying container (to say, ArrayList) without affecting pages and pages of code.

You use the same general form when you have more than one generic parameter. We can instantiate a Table to work with String keys, and integer values that are phone numbers:

Table<String,Integer> phonebook = new Table<String,Integer> ( );

To summarize what's going on here:

  • The left-hand side says “phonebook” is an object of type Table parameterized with String, Integer to match parameters names Key,Value respectively. Refer back to the code in the box headed “The Table Example”.

  • The right-hand side calls a constructor of the Table<String, Integer> class.

  • The Table-object-referenced-by-phonebook has been specialized so that it can only store values that are Integer objects. The integers are accessed by keys that are Strings.

  • When you see “Type <OtherType>” you usually read it as “this is a Type of OtherType's”, so here, “this is a Table of String/Integer pairs”. Since String is the key, we know it will be the phone subscriber's name. (This example works in small cases, but falls apart in real world scenarios of multiple phone lines and multiple people with the same name).

Invoking methods on objects of the instantiated generic type

If we also want to create a Table that can hold Strings that are accessed by Timestamps keys, we can use the same generic Table, and give it different parameters like this:

Table<Timestamp,String> appts = new Table<Timestamp,String> ( );

The appts object holds a schedule of appointments throughout a day, keyed by timestamp, each with a String saying what the appointment is at that time. You then use the newly created appts instance in the normal way, without regard to how its class started off as a generic class. You might add a few appointments by invoking Table's put() method like this:

appts.put(new Timestamp(7,30,00), "Feed dogs");
appts.put(new Timestamp(9,30,00), "Dentist");
appts.put(new Timestamp(12,00,00), "Lunch with Bob");

The appts Table is happy to hold <Timestamp, String> values.

The phonebook Table holds <String, Integer> pairs. If you forget what you're doing, and try to put a Timestamp, String pair into the phonebook Table:

phonebook.put(new Timestamp(9,30,00), "Dentist");

you'll get a compiler error message like this: “Error: put(String,Integer) in Table<String, Integer> cannot be applied to (Timestamp, String)”. Before generics, when Tables just held Objects, this level of type checking was not possible. With generics, Tables still deal with Objects at run-time, but the extra information at compile time allows stronger type checking. If you use generic parameters for all your collections, and all your program compiles without errors or warnings, and you don't use native code, then your program is guaranteed not to fail at run-time with a class cast exception on a collection.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.58.194