The JavaTM Tutorial
Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Trail: Learning the Java Language
Lesson: Classes and Inheritance

Generics

Consider the following classes (written before the introduction of generics in JDK 5.0), designed to describe various kinds of media that a library might contain:
public class Library {
    private List resources = new ArrayList();
    public void addMedia(Media x) {
        resources.add(x);
    }
    public Media retrieveLast() {
        int size = resources.size();
        if (size > 0) {
            return resources.get(size - 1);
        }
        return null;
    }
}

public class Media {
}

public class Book extends Media {
}

public class Video extends Media {
}

public class Newspaper extends Media {
}
Given the above classes, imagine a program that uses them to catalog information about a collection of books (and only books). Here is a code snippet from that program:
//A Library containing only Books
Library myBooks = new Library();
...
Book lastBook = (Book)myBooks retrieveLast();
Type casting the return value from the retrieveLast method to Book is necessary, so the compiler knows what kind of object is returned and what kinds of operations can be performed on that object. Even though the programmer may be certain that only Book objects will be returned, the compiler does not know this. If a programming error did result in, say, a Video being stored in myBooks and then returned here, the cast would cause an unexpected exception to be thrown at runtime.

This is the primary reason that generic types and methods (often referred to, simply, as generics) were added to the Java language — to add compile-time type checking so the compiler can detect mismatched types at compile time, rather than runtime, when an exception is thrown.

Here is the same Library class with a few simple changes (shown in bold), modified to use generics:

public class Library<E> {
    private List resources = new ArrayList<E>();
    public void addMedia(E x) {
        resources.add(x);
    }
    public E retrieveLast() {
        int size = resources.size();
        if (size > 0) {
            return resources.get(size - 1);
        }
        return null;
    }
}

//The Media classes require no changes to use generics —
//please refer to the original example.
Library is now a generic type with a single type parameter E. When using a generic type, specify a type argument for each of its type parameters. A Library containing only Book objects, for example, would be written as Library<Book>. This is an example of a parameterized type. The code snippet now looks like this:
Library<Book> myBooks = new Library<Book>();
...
Book lastBook = myBooks retrieveLast();
The type cast and the comment are no longer needed because the Library instance has been constrained to deal with Book objects. The compiler now understands that this particular instance of Library contains Books. If the compiler detects that the Library is being used inappropriately — trying to add or retrieve a Newspaper, for example — it generates an error.

Later on our imaginary program includes the following line of code:

myBooks.addMedia(myFavoriteBook);
Without generics, the compiler couldn't warn you if myFavoriteBook is of the wrong type. If it is a String, for example, a runtime exception would occur somewhere down the road after someone invokes retrieveLast. These sorts of errors can be especially hard to debug, since the runtime error may occur at a point in the code far removed from the actual programming error that caused it. With generics, the compiler calls out the error right away. You find out about the programming error immediately, and are shown exactly where the problem lies.

You can further refine the declaration of Library to use Library<E extends Media>. The compiler will enforce the constraint that a Library can only contain Media objects (and not, say, Numbers).

If you use any of the Java programming libraries that deal with groups of data, such as classes or interfaces that deal with collections, lists, arrays, and vectors, you will notice that they make extensive use of generics.


Note: When compiling code that refers to generics, you might encounter a message like this:
Note: MyFile.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
This error means that you have bypassed generics and have lost the benefit of compile-time type checking. You should fix your code to make full use of generics and take advantage of compile-time type checking.

As mentioned in the text of the sample error, using the -Xlink:unchecked javac option gives you the most information available about unchecked or type unsafe operations.


This covers a simple case of using a generic class. Subsequent sections go into generic types in more detail and touch on generic methods.


Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Copyright 1995-2005 Sun Microsystems, Inc. All rights reserved.