THE Java™ Programming Language, Fourth Edition

(Jeff_L) #1

// ...
else if (type == ChessPiece.QUEEN)
return queenReachable(current);
else
throw new Error("Unknown type");
}


The job of reachable is to dispatch actual calculations to methods that know how to handle specific chess
pieces, so it has to consider all the possible values of the chess piece type that was passed to it. Whenever you
see a chain of ifelse statements, as above, or equivalently a switch statement (which you'll learn about in
Chapter 10) that distinguishes different objects or types of object, ask yourself if there is a way to have the
object decide what needs to be doneafter all, each object knows what it is. In this case, why can't you ask the
chess piece for its set of reachable positions? It turns out you canby adding constant-specific behavior to the
enum:


enum ChessPiece {
PAWN {
Set reachable(Position current) {
return ChessRules.pawnReachable(current);
}
},
ROOK {
Set reachable(Position current) {
return ChessRules.rookReachable(current);
}
},
// ...
QUEEN {
Set reachable(Position current) {
return ChessRules.queenReachable(current);
}
};


// declare the methods defined by this enum
abstract Set reachable(Position current);
}


This time each named enum constant is followed by a class body that defines the methods for that enum
constantin this case dispatching to the appropriate ChessRules method as before. The advantage of
dispatching in the enum constants themselves is that you can't forget to handle the case for a specific value.
Looking at the original code for reachable you can see that it would be easy to leave out one of the values,
only realizing this at runtime when the error is thrown. Also note that even though we have covered all the
possible values in reachable, we still must have that final else clause because the compiler won't check
to see if we have covered all possible values and the method must either return a value or throw an exception.
By using constant-specific methods we moved the checking for completeness to compile time, which is
always a good thing, and we avoided writing code that should never get executed.


The classes that are defined for each enum constant are effectively anonymous inner classes that extend the
enclosing enum typewhich is why you can't actually declare an enum to be final: Even though it can't be
directly extended, it can be implicitly extended by these anonymous inner classes.


As with other anonymous inner classes, the enum constant class body can define arbitrary instance fields, and
methods, but it can't declare static members or define constructors. Also note that because enum constants are
implicitly static fields, these anonymous inner classes have no enclosing instance.

Free download pdf