Generics

Generic programming is the technique of producing code that will work with many different types of data. Earlier we looked at we looked at dynamic arrays of integers. The code given there only works for data of type int. However, the code required for dynamic arrays of double, String or any other type would be very similar, requiring only that one type name be substituted for another.

It doesn’t seem like a good idea to write virtually the same code several times. Java helps us to avoid this by providing the ArrayList class, since an ArrayList is effectively a dynamic array of values of type Object. Since every class is a subclass of Object, values of any type can be stored in an ArrayList. Java also provides parameterized types. The ArrayList type can be parameterized, eg: ArrayList<String>, to restrict the values that can be stored in the list to objects of a specified type.

The ArrayList class is one of several standard classes used for generic programming in Java. We will now examine these classes and how they are used in more detail, and we’ll also take a look at generic methods and generic interfaces. All the classes and interfaces discussed below are defined in the package java.util, so you will need an import statement at the beginning of your program to get access to them, eg:


import java.util.*.

Later, we will see that it is also possible to define new generic classes, interfaces and methods, but first we’ll make use of the generics that are predefined in Java’s standard library.

The first version Java had only a few generic data structure classes, such as Vector, that could hold values of type Object. Java 1.2 introduced a much larger group of generics, known as the Java Collection Framework, that followed the same basic model. The ArrayList class is part of the Collection Framework.

In the original Collection Framework, a data structure designed to hold Objects could be used with objects of any type. This approach is prone to generating errors that only appear at run time, rather than at compile time. If a programmer assumes that all the items in a data structure are strings and tries to process them in line with this assumption, a run-time error will occur if other types of data have been added to the data structure. The error will probably arise when the program retrieves an Object from the data structure and tries to type-cast it to type String. If the object is not actually of type String, this will cause an error of type ClassCastException.

Java 5.0 introduced parameterized types, such as ArrayList<String>. This allowed programmers to create generic data structures that could be type-checked at compile time rather than at run time. Type-casting is no longer necessary, so ClassCastExceptions are avoided. The compiler will detect any attempt to add an object of the wrong type to the data structure, report a syntax error, and refuse to compile the program.

In Java 5.0, all of the classes and interfaces in the Collection Framework, as well as some others, have been parameterized. In this course we will make use of use the parameterized types, but you should remember that their use is not mandatory. It is still legal to use a parameterized class as a non-parameterized type, such as a plain ArrayList.

With a Java parameterized class, there is only one compiled class file, eg: there is only one compiled class file, ArrayList.class, for the parameterized class ArrayList. The parameterized types ArrayList<String> and ArrayList<Integer> both use the same compiled class file, as does the plain ArrayList type. The type parameter tells the compiler to restrict the type of object that can be stored in the data structure. The type parameter has no effect at run time.

Java’s generic data structures can be divided into two categories: collections and maps. A collection is simply a collection of objects. A map associates objects in one set with objects in another set. It is analogous to the way that a telephone directory associates names with telephone numbers.  In Java, collections and maps are represented by the parameterized interfaces Collection<T> and Map<T,S>, where T and S stand for any type except for the primitive types.  Note that Map<T,S> is the first example we have seen where there are two type parameters. We’ll look more closely at maps later, but, for the moment, we’ll stick to collections.

Next: Lists and Sets