A Gentle Introduction to Generics in Java by Charlie Calvert

By: Charlie Calvert

Abstract: Read a gentle introduction to the basic facts about an exciting new feature found in the Java 1.5 beta. Formerly generics were only available to C++ and Python programmers. Now Java has them too!

Copyright (c) 2004 by Charlie Calvert

This article is designed to show you the simplest possible example of creating a Generic class in Java 1.5. The text was written against Java 1.5 Beta 1, which can be downloaded generically from https://java.sun.com, or more specifically from https://java.sun.com/j2se/1.5.0/download.jsp.

There are various complexities involved with using generics, and various philosophical issues surrounding the particular implementation of generics chosen by the designers of languages such as Java or C#. In this article I'm going to side step those issues and just show you the basics of how to use generics. I will, however, emphasize the role that type checking plays in both the Java and C# implementation of generics. This emphasis skirts around the edges of several controversial matters without ever quite cluttering this article with confusing issues not relevant to an understanding of the basics.

Generics in Java

The primary goal of generics in languages such as C++ and Java is to allow a single class to work with a wide variety of types. For instance, the ArrayList object has always had the ability to store a list of any type of class. However, ArrayList has always forced you to typecast the objects that you pulled out of the list:

String myString = (String)myArrayList.get(0);

This system effectively destroys the benefits of a strongly typed language. Java statements like the one just shown end up nullifying the safety that accompanies built in type checking. As a result, the generic version of the ArrayList class would be designed to work natively with any type of class, put to preserve the benefits of type checking.

With the generic version of the ArrayList class, your code would look like this:

String myString = myArrayList.get(0);

Notice the abscence of the typecast. It would be a mistake, however, to assume that you could assign anything to the return value of the get method without needing a typecast. If you tried to assign anything else besides a String to the output of the get method, you would encounter a compile time type mismatch such as this one:

found   : java.lang.String
required: java.lang.Integer
Integer data = list.get(0);
^

Take a brief look at the complete code for the example we have just been discussing, but don't dwell on it too long:

	ArrayList <String> list = new ArrayList<String>();
list.add("Generic String");
String data = list.get(0);
JOptionPane.showMessageDialog(this, data);

Glancing at this code, you probably noticed the word <String> appearing in brackets. You can think about that syntax as simply being a way of telling the Java compiler that you want this generic ArrayList to hold objects of type String. You are, in a sense, binding the ArrayList object to the String type via the use of this syntax. After you have done this, it would be illegal to try to bind an Integer or some other non-String type to the result of the get function:

int data = list.get(); // illegal!!! must be a String because of ArrayList <String> list declaration.

Declaring Your Own Generic Class.

For me, the easiest way to come to grips with generics, or many other new technologies, is to see a very simple example. Starting from this foundation, I can begin to build a deeper understanding of how the technology works. Below you will find the declaration for a very simple generic class called BasicGeneric. Beneath it you will find another simple class called GenSample that contains two methods, test01 and test02, that exercise the BasicGeneric class. Note that this second class has a static main method so that it can be run as an application. I suggest putting both classes in a single file called GenSample.java.

When compiling the example shown in Listing 1, you must add source "1.5" when invoking javac. It is probably also helpful to add the -version flag to the call. In most SDKs, version was not supported in Javac before 1.5, so it can help alert you if you are accessing the wrong version of the binary:

javac -version -source "1.5" -sourcepath src -d classes src/applet_generics01/AppletGenerics.java

Listing 1: GenSample.java contains a very simple generic class called BasicGeneric, and a second class called GenSample that calls it.

class BasicGeneric <A>
{
private A data;

public BasicGeneric(A data)
{
this.data = data;
}

public A getData()
{
return data;
}
}

public class GenSample
{
public GenSample()
{
}

public String test01(String input)
{
String data01 = input;

BasicGeneric<String> basicGeneric = new BasicGeneric<string>(data01);
String data02 = basicGeneric.getData();

return data02;
}

public int test02(int input)
{
Integer data01 = new Integer(input);

BasicGeneric <Integer> basicGeneric = new BasicGeneric<Integer>(data01);
Integer data02 = basicGeneric.getData();
return data02;
}

public static void main(String [] args)
{
GenSample sample = new GenSample();
System.out.println(sample.test01("This generic data"));
System.out.println(sample.test02(12));
}
}

Notice the declaration for BasicGeneric:

class BasicGeneric <A>

Here you can see the brackets that surround the capital letter A: <A>.  This syntax specifies that the class is a generic type. Notice also that the class declares a variable of type A:

    private A data;

This syntax can be confusing at first glance. But it is quite sensible when you begin to understand generics. BasicGeneric does not work with any specific type. As a result, we don't assign a type to A. It is a generic type. But when you declare an instance of this class, you must specify the type with which you want to work:

BasicGeneric<String> basicGeneric

Notice the <String> syntax after the declaration BasicGeneric. That is where you declare that this instance of BasicGeneric is going to work variables of type String. You can also ask it work with variables of type Integer, or of any other type that catches your fancy. Here is an example of how to declare an instance of this type that will work with an Integer:

BasicGeneric<Integer> basicGeneric

Once you understand this much, the rest should start to fall into place. Notice, for instance, the declaration of the getData method:

    public A getData()
{
return data;
}

This method returns a value of type A, which is to say that it will return a generic type. But that does not mean that the function will not have a type at run time, or even at compile time. After you declare an instance of BasicGeneric, you will have specified the type of A. After that, BasicGeneric will act as if it were declared from the very beginning to work with that specific type, and that type only.

You should now be able to look at the two instances of using the BasicGeneric type and make sense of them:

	BasicGeneric<String> basicGeneric = new BasicGeneric<string>(data01);
String data02 = basicGeneric.getData();

BasicGeneric <Integer> basicGeneric = new BasicGeneric<Integer>(data01);
Integer data02 = basicGeneric.getData();

In one case the BasicGeneric type is designed to work with Strings, in the other case it is designed to work with Integers. What could be simpler or more straight forward?

Summary

In the end, the basic facts about generics turn out to be fairly easy to understand. The syntax for using them is clean, and relatively intuitive once you get over a few fairly low conceptual hurdles. The power of the syntax should also be fairly obvious.

I should add, however, that this subject can get a bit trickier once you start digging beneath the surface that we have mapped out in this simple introductory article. Also, there are certain intentional limitations in the Java and C# implementations of generics which are not inherent in the C++ implementation. Hopefully in the future I will find time to explain some of these complexities.

However, it should be clear that the basic syntax for using this tool is simple and straight forward. It should also not be hard to imagine some obvious ways to use this syntax in your own programs. The best thing to do then, is to download a copy of the Java J2SE 1.5 and copy my example and get to work experimenting with this very fascinating new tool.


Server Response from: ETNASC04