27 JANUARY, 2005
Java generics

By John Hunt

The Java 2 Platform Standard Edition 5.0 Development Kit (JDK 5.0) was released during September 2004. It extended the Java language in a number of ways. One particular way was to add the concept of Generics to the Java language. At this point, you may well ask "what are generics?". They are essentially data structures that can be configured for a particular data type at run time. To see why generics are useful consider the following piece of Java code:

package com.planetjava.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
  * Illustrates pre generics loop processing
  *
  **/
public class LoopExample {
    public static void main(String [] args) {
      List list = new ArrayList();
      list.add("John");
      list.add("Denise");
      Iterator it = list.iterator();
      while (it.hasNext()) {
        String st = (String)it.next();
        System.out.println(st);
      }
    }
}

This program adds two strings to an array list collection object. We then use an iterator to loop through the contents of the list. Each element in the list is retrieved (via the it.next() statement). However, to store it in a string it must be cast back to a string. This is because, although the object referenced by the ArrayList list, is a string, the only thing that can be guaranteed by Java at the point of retrieval form the list is that it is an object. This works but makes for more complex code as well as offering the possibility that the list may contain other types of object. We could of course protect against this by adding an instanceof test to ensure the object is a string, for example:

import java.util.*;

public class LoopExample {
  public static void main(String [] args) {
    List list = new ArrayList();
    list.add("John");
    list.add("Denise");
    Iterator it = list.iterator();
    while (it.hasNext()) {
      Object obj = it.next();
      if(obj instanceof String) {
        String st = (String)obj;
        System.out.println(st);
      }
    }
  }
}

However, with generics, it is possible to mark a collection as only be allowed to hold a particular type of data. We will look at this in the next section.

Using Generics with collections
Generics allow a collection to be restricted to a particular data type in Java. That is, to a particular class or interface type. This allows code to be less cluttered as well as allowing the compiler to perform additional compile time type checking.

If we consider the program presented earlier and look at what its generic based version would be:

import java.util.*;

public class GenericsExample {

  public static void main(String [] args) {
    List<String> list = new ArrayList<String>();
    list.add(new String("John"));
    list.add(new String("Denise"));
    Iterator<String> it = list.iterator();
    while (it.hasNext()) {
      String st = it.next();
      System.out.println(st);
    }
  }
}

Both versions of the program will print out two strings to the standard output (e.g. command window or console). The only difference is in the programs themselves. In the Generics example, we have marked the List as only being allowed to contain Strings. This is now a list of strings – and what’s more, the complier and runtime environments will ensure that this is the case. This when we retrieve a string form the list we do not need to cast it to a String – Java already knows it will be a string.

At first sight, you may think that all we have done is to move some of the syntax around. After all, we have still had to specify that the List will hold only strings and that the Iterator will iterate over string objects. Surely, this just means we have sugared the language with a slightly different way of casting our objects.

Although at first glance this appears to be what has happened, the implications are far deeper (as was hinted at above). The Java environment now knows that this is not just a List but a List of Strings and will check at compile time to ensure only Strings are being added to the list and at run time to ensure that only Strings are actually added to the list. This should of course improve the reliability of Java programs in general (and may also improve the readability as you will know the type of object intended for a list when the list is declared – rather then when the object is retrieved at some point later on!).

It is worth sticking with the use of generics with collections a little longer to examine one more feature of generics. What is created when you make an instance of a collection using generics. For example, if we write:

List<Integer> intList = new ArrayList<Integer>();
List<String> stringList = new ArrayList<String>();

What type of object is referenced by the variables intList and stringList? Are they instances of a class ArrayList<Integer> and ArrayList<String>, instances of some other unspecified class or instances of ArrayList?

One way to test this is to execute the following two statements:

System.out.println(intList.getClass().getName());
System.out.println(stringList.getClass().getName());

The result of executing these two statements is:

java.util.ArrayList
java.util.ArrayList

Which may surprise you. The reason that both intList and stringList are instances of the class ArrayList is that they are both ArrayList objects but they have been parameterised to only hold certain types of object. That is they are both ArrayLists but one has been told to only hold Integer objects and the other to only hold String objects.

Generics in declarations
Generics are very useful with collections of objects and you will find that the new Iterator interface and the new collection classes in the Collections class hierarchy have all been updated to allow them to be parameterised for a specific type of object. Of course, it is not only when using collections that generics may be useful. What if you are creating your own data type and what to allow programmers to parameterise that as well? You can do exactly this using some new syntax added to Java.

Let us suppose we wish to create a very simple Queue class (the J2SE version 5.0 has added some Queue support finally but we will ignore that for the moment). This queue class will define three methods, add, isEmpty and pop. This is a very simple queue class as I do not want to confuse the issue with other data structures or inheritance. Instead, this queue is extremely limited as it can only hold a single item of data at a time:

import java.util.*;

public class Queue<E> {
  private E data;

  public void add (E x) {
    data = x;
  }

  public boolean isEmpty() {
    return data == null;
  }

  public E pop() {
    return data;
  }

}

If we examine, the definition of this new class we can see a number of things. Firstly, the name of the class appears to be Queue<E>. Actually, the "E" in the angle brackets is the declaration of the formal type parameter for our Queue (note that I have selected E as that is what is used within the collections classes in Java, however, any letter could be used, for example, I could have chosen X).

The next thing to note is that the type of the instance variable data is "E" ( the formal type parameter from the class name). Thus, the type of "data" will be determined at compile time based on how the Queue class is parameterised. If we say later on:

Queue<String> q = new Queue<String>();

Then the type of "data" will be String. In a similar manner the parameter of the method add is also E. And thus, the actual type of the parameter will be determined when Queue is used. This is also true of the method pop, which has a return type of E.

In many ways, what we are doing is partially defining our Queue. We are leaving out the exact type of data to be held, passed in and returned until the Queue class is actually used in the code.

So, we can now write a main method test harness that uses the new Queue class thus:

public static void main(String [] args) {
Queue<String> q = new Queue<String>();
q.add("John");
System.out.println(q.pop());
}

Generics in Java
A quick word of caution about the concept of generics - from the above description it may appear that what we are doing is creating a template for a class that is in some ways made into a concrete class when we use it. That is that Queue<E> is a template for a QueueString, QueueInteger or QueuePerson class. Whilst this may be a useful analogy to draw, it is not technically accurate. This is because Java does not actually create different versions of the Queue class. Thus, just as with ArrayList, there is only one Queue class in the system, but in this case they have been instantiated to hold Strings, Integer or Person objects.




   


Get 6 FREE copies of "Application Development Advisor" magazine. This offer is open to IT professionals based in the United Kingdom. Coverage of .NET, XML and databases by experts.
www.appdevadvisor.co.uk
Visit Solutions Architect, our new website covering service-oriented infrastructures. Read articles and sign-up to receive the weekly emails.
www.solutionsarchitect.co.uk
Visit RFID Today, our new website covering Radio Frequency Identification. Read the latest articles and case studies. Sign-up to receive RFID Today magazine.
www.rfidtoday.co.uk

ADA Communications, Charwell House,
Wilsom Road, Alton, Hampshire, GU34 2PP, UK.
Tel: +44 (0)1420 594200
www.adacom.co.uk

© Copyright 2001 - 2005 by ADA Communications Ltd. All rights reserved. Statements of opinion and fact are made on the responsibility of the authors alone and do not imply an opinion on the part of ADA Communications Ltd or the editorial staff. Registered in England No. 04843018