8.3. "There Is No Spoon"
In the movie
The Matrix
,
[*]
the hero Nero is
The design goals for Java generics were formidable: add a
To accomplish this feat, Java employs a technique called
erasure
, which
8.3.1. ErasureLet's take a look at a compiled generic class: our friend, List . We can do this easily with the javap command:
%
javap java.util.List
public interface java.util.List extends java.util.Collection{
...
public abstract boolean add(java.lang.Object);
public abstract java.lang.Object get(int);
The result looks exactly like it did prior to Java generics, as you can confirm with any older version of the JDK. Notably, the type of elements used with the
add( )
and
get( )
List<Date> dateList = new ArrayList<Date>( );
System.out.println( dateList instanceof List ); // true!
But our generic dateList clearly does not implement the List methods just discussed: dateList.add( new Object( ) ); // Compile-time Error! This illustrates the somewhat schizophrenic nature of Java generics. The compiler believes in them, but the runtime says they are an illusion. What if we try something a little more sane looking and simply check that our dateList is a List<Date> :
System.out.println( dateList instanceof List<Date> ); // Compile-time Error!
// Illegal, generic type for instanceof
This time the compiler simply puts its
What has really
We can now answer one of the questions we posed at the beginning of the section ("Why can't I implement what appear to be two different generic interfaces in one class?"). We can't have a class that implements two different generic
List
public abstract class DualList implements List<String>, List<Date> { }
// Error: java.util.List cannot be inherited with different arguments:
// <java.lang.String> and <java.util.Date>
8.3.2. Raw Types
Although the compiler treats different parameterizations of a generic type as truly different types (with different APIs) at compile time, we have seen that only one real type exists at runtime. For example, the class of
List<Date>
and
List<String>
share the plain old Java class
List
.
List
is called the
raw type
of the generic class. Every generic has a raw type. It is the
Java 5.0 has been
It is still possible to use raw types in Java 5.0 just as before. The only difference is that the Java 5.0 compiler generates a new type of warning wherever they are used in an "unsafe" way. For example:
// nongeneric Java code using the raw type, same as always
List list = new ArrayList( ); // assignment ok
list.add("foo"); // unchecked warning on usage of raw type
This snippet uses the raw List type just as any good old-fashioned Java code prior to Java 5.0 would have. The difference is that now the Java compiler issues an unchecked warning about the code if we attempt to insert an object into the list.
%
javac MyClass.java
Note: MyClass.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
The compiler instructs us to use the -Xlint:unchecked option to get more specific information about the locations of unsafe operations:
%
javac -Xlint:unchecked MyClass.java
warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.
List: list.add("foo");
Note that creating and assigning the raw ArrayList do not generate a warning. It is only when we try to use an "unsafe" method (one that refers to a type variable) that we get the warning. This means that it's still okay to use older style nongeneric Java APIs that work with raw types. We only get warnings when we do something unsafe in our own code.
One more thing about erasure before we move on. In the previous examples, the type variables were replaced by the
Object
type, which could represent any type
class Bounded< E extends Date > {
public void addElement( E element ) { ... }
}
This parameter type declaration says that the element type E may be any subtype of the Date type. In this case, the erasure of the addElement( ) method can be more restrictive than Object , and the compiler uses Date :
public void addElement( Date element ) { ... }
Date is called the upper bound of this type, meaning that it is the top of the object hierarchy here and the type can be instantiated only on type Date or on "lower" (more derived) types. Now that we have a handle on what generic types really are, we can go into a little more detail about how they behave. |