Java Enum definition
This can be illustrated by a simple example and a technique which can be used to implement chained method calls for sub-classes. In an example below setName
returns a Node
so chaining won't work for the City
:
class Node {
String name;
Node setName(String name) {
this.name = name;
return this;
}
}
class City extends Node {
int square;
City setSquare(int square) {
this.square = square;
return this;
}
}
public static void main(String[] args) {
City city = new City()
.setName("LA")
.setSquare(100); // won't compile, setName() returns Node
}
So we could reference a sub-class in a generic declaration, so that the City
now returns the correct type:
abstract class Node<SELF extends Node<SELF>>{
String name;
SELF setName(String name) {
this.name = name;
return self();
}
protected abstract SELF self();
}
class City extends Node<City> {
int square;
City setSquare(int square) {
this.square = square;
return self();
}
@Override
protected City self() {
return this;
}
public static void main(String[] args) {
City city = new City()
.setName("LA")
.setSquare(100); // ok!
}
}
The following is a modified version of the explanation from the book Java Generics and Collections:
We have an Enum
declared
enum Season { WINTER, SPRING, SUMMER, FALL }
which will be expanded to a class
final class Season extends ...
where ...
is to be the somehow-parameterised base class for Enums. Let's work
out what that has to be. Well, one of the requirements for Season
is that it should implement Comparable<Season>
. So we're going to need
Season extends ... implements Comparable<Season>
What could you use for ...
that would allow this to work? Given that it has to be a parameterisation of Enum
, the only choice is Enum<Season>
, so that you can have:
Season extends Enum<Season>
Enum<Season> implements Comparable<Season>
So Enum
is parameterised on types like Season
. Abstract from Season
and
you get that the parameter of Enum
is any type that satisfies
E extends Enum<E>
Maurice Naftalin (co-author, Java Generics and Collections)
It means that the type argument for enum has to derive from an enum which itself has the same type argument. How can this happen? By making the type argument the new type itself. So if I've got an enum called StatusCode, it would be equivalent to:
public class StatusCode extends Enum<StatusCode>
Now if you check the constraints, we've got Enum<StatusCode>
- so E=StatusCode
. Let's check: does E
extend Enum<StatusCode>
? Yes! We're okay.
You may well be asking yourself what the point of this is :) Well, it means that the API for Enum can refer to itself - for instance, being able to say that Enum<E>
implements Comparable<E>
. The base class is able to do the comparisons (in the case of enums) but it can make sure that it only compares the right kind of enums with each other. (EDIT: Well, nearly - see the edit at the bottom.)
I've used something similar in my C# port of ProtocolBuffers. There are "messages" (immutable) and "builders" (mutable, used to build a message) - and they come as pairs of types. The interfaces involved are:
public interface IBuilder<TMessage, TBuilder>
where TMessage : IMessage<TMessage, TBuilder>
where TBuilder : IBuilder<TMessage, TBuilder>
public interface IMessage<TMessage, TBuilder>
where TMessage : IMessage<TMessage, TBuilder>
where TBuilder : IBuilder<TMessage, TBuilder>
This means that from a message you can get an appropriate builder (e.g. to take a copy of a message and change some bits) and from a builder you can get an appropriate message when you've finished building it. It's a good job users of the API don't need to actually care about this though - it's horrendously complicated, and took several iterations to get to where it is.
EDIT: Note that this doesn't stop you from creating odd types which use a type argument which itself is okay, but which isn't the same type. The purpose is to give benefits in the right case rather than protect you from the wrong case.
So if Enum
weren't handled "specially" in Java anyway, you could (as noted in comments) create the following types:
public class First extends Enum<First> {}
public class Second extends Enum<First> {}
Second
would implement Comparable<First>
rather than Comparable<Second>
... but First
itself would be fine.