If you take a look at the definition of the java.lang.Enum
class in Java 5 or later, you’ll
see a rather bizarre-looking generic type declaration:
Enum
<
E
extends
Enum
<
E
>
>
{
...
}
In trying to parse this, you may be hampered by two thoughts, which
we’ll try to dispel right away. First, upon quick inspection this may
appear to be recursive. The type variable E
seems to be defined as something that’s not
yet finished being defined. But it’s not really. We often have
mathematical equations of the form x = function(
x )
and they are not recursive. What they really call for is a
special value of x
that satisfies the
condition. Next, although it’s pretty clear that E
is a subtype of some formulation of the
generic Enum
type, you may jump to the
conclusion that E
itself must be a
generic type. Remember that concrete types can extend generics just as
well as generics can.
With these thoughts in mind, let’s hunt for some arrangement that satisfies these bounds. Let’s focus only on the bound for a moment:
E
extends
Enum
<
E
>
E
is a subclass of some
parameterization of Enum
and, in
particular, the parameterization of Enum
on the subclass type itself. To say this
again, what it does is to require that any invocations of the Enum
type are by subclasses of some
parameterization of the Enum
type. And
specifically, the parameterizations of the Enum
type supply their own type as the type
parameter to their parent, Enum
. What
kind of class satisfies this condition?
class
Foo
extends
Enum
<
Foo
>
{
}
This Foo
class does. The
declaration of Foo
, in fact, reads just
as the bound does. Foo
is a plain
concrete type that extends Enum
parameterized by its own type.[24]
What does this accomplish exactly? The first implication of this
arrangement is that Enum
can be
instantiated only by subclasses of itself. Next, we have the condition
that the Enum
must be instantiated with
the child type as its parameter type. This means that any methods of the
parent Enum
class that refer to the
type variable E
will now refer to the
child type. This peculiar bound has guaranteed that
child types customize their parent with their own type. In fact, this is
exactly what the Enum
class in Java
needs in order to make enums work. The compareTo()
method of a
Java enum refers to the type variable and is intended to be applicable
only to other instances of the specific child enum
type:
public
int
compareTo
(
E
e
)
{
...
}
For example, a Dog
enum type
should be able to compare only types of Dog
and comparing a Dog
with a Cat
should produce a compile-time error. The
bound accomplishes just that by adapting the compareTo()
method to the Dog
type:
class
Dog
extends
Enum
<
Dog
>
{
...
}
Normally, a nonfinal base class, having no way to know what children
it may have in the future, could only refer to its own type as a general
supertype for all of the children when it wants to work with others of its
own kind. Methods of a nongeneric Enum
class could only supply methods that work on any Enum
. But through the magic of generics, we can
effectively change the API of the class based on how it is invoked with
parameters. In this case, we have arranged that all subclasses must supply
themselves as the parameter for the base class, tailoring its methods to
themselves and pushing the base type down a generation.
[24] In real life, Java doesn’t let us extend the Enum
type; that’s reserved for the enum
keyword and the compiler. But the
structure is as shown.
Get Learning Java, 4th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.