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

Using Generic Types

You'll usually see generics when dealing with collections of some kind. In fact, the Collections Framework (in the Learning the Java Language trail) (which we'll call Collections from now on) was a major motivation behind the introduction of generic types to the Java language because generics enable compile-time checking of the type safety of operations on a collection. When you specify the type of object stored in a collection:

Type Parameter Conventions

You have already seen the angle bracket and single letter notation used to represent a type parameter. By convention, a type parameter is a single, uppercase letter — this allows easy identification and distinguishes a type parameter from a class name. The most common type parameters you will see are:

You can invoke a generic type with any class, but you cannot invoke a generic type with a primitive type, such as int.

Generics and Relationships Between Types

You might expect that ArrayList<Object> is a supertype of ArrayList<String>, because Object is a supertype of String. In fact, no such relationship exists for instances of generic types. For example, consider the following code snippet:
List<String> ss = new ArrayList<String>(1);
List<Object> os = ss; //WRONG. This causes a compile error.
You might think that since String is a subclass of Object that assigning ss to os shouldn't be a problem.

But suppose that the above assignment was allowed and you then added an object to os:

os.add(new Object());
If you later tried to retrieve the object from ss:
ss.get(0);
the Object that is returned isn't guaranteed to be a String — this clearly violates how ss was defined. The compiler does not allow you to make any assignment that may compromise type safety.

Generics and Type Erasure

Although the previous section says that the compiler doesn't allow you to make any assignment that may compromise type safety, there is one important exception to know about.

When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a class or method. Type erasure means that Java applications that use generics maintain binary compatibility with Java libraries and applications created before generics.

For instance, Iterator<String> is translated to type Iterator, which is called the raw type — a raw type is a class without a type argument. This means that you can't find out what type of Object a generic class is using at runtime. The following operations are not possible:

public class MyClass<E> {
    public static void myMethod(Object item) {
        if (item instanceof E) {  //Compiler error
            ...
        }
        E item2 = new E();   //Compiler error
        E iArray[] = new E[10]; //Compiler error
        E obj = (E)new Object(); //Unchecked cast warning
    }
}
The operations shown in red are meaningless at runtime because the compiler removes all information about the actual type argument (represented by the type param E) at compile-time.

Type erasure exists so that new code may continue to interface with legacy code, but it should not otherwise be considered good programming practice.


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.