Cell
page 267).
The fact that there is a single class definition for a generic class, no matter how many different parameterized
invocations there may be, has several consequences in regard to what you can and cannot do with generics.
We point these out along the way, but they all boil down to this: There is only one class, so you cannot do
anything that cannot be done with only one class.
The first consequence of this fact is that a generic class with a type parameter, say E, cannot use E in the type
of a static field or anywhere within a static method or static initializer. To do so would require a different field
or method for each value of E. Because there is only one class, there is only one actual static field or method,
and so the code cannot depend on the value of E. The fact that static members are never dependent on a
particular parameterization is reinforced by the rule that you cannot refer to a static member via a
parameterized type name. For example, if SingleLinkQueue has a static merge method, it must be
invoked as SingleLinkQueue.merge. SingleLinkQueue
not compile. To similarly reinforce that there is but a single class object, a class literal such as
SingleLinkQueue.class can only use the raw class name.
Within a generic class definition, a type parameter can appear in any non-static declaration where you would
normally put a concrete type name: in a field declaration, a method return type or parameter type declaration,
a local variable declaration, or a nested type declaration. You've seen examples of most of these uses in the
Cell and SingleLinkQueue classes.
Two places where you would usually use a class name but cannot use a type parameter are when creating
objects and arrays. For example, here is the wrong way to try to expose the elements of a queue as an array:
class SingleLinkQueue
// ...
public E[] toArray() {
int size = 0;
for (Cell
size++;
E[] arr = new E[size]; // INVALID: won't compile
// ... copy in elements ...
}
}
You cannot instantiate E or create an array of E for the same reason you can't have a static field of type E:
There is a single class with a single definition of toArray, and the compiler has to know at compile time
what code to generate to create any objects or arrays. There is no way for the compiler to generate
newString[size] or newInteger[size] depending on what E happens to be.[1]
[1] This is a consequence of there being no parameterized type information within objects at
runtimesee Section 11.5 on page 267.
By now you may be wondering how it is that the compiler can do anything with type parameters? The answer
to that is deceptively simple: The compiler uses the most general possible type to represent the type parameter
(often Object) and uses type casting to ensure type correctness. You can think of generics as a (potentially
optimized) shorthand for writing all those casts. This is explained in more detail a little later.
So how do you expose the elements of the queue as an array? You could expose the size of the queue and
have the caller create, and pass to you, an array of the right size and typewhich we show you a little lateror
you could have the caller provide the type token for E (the Class object for the actual type argument) and