Java 1.5 Tiger A Developers Notebook [Electronic resources]

David Flanagan, Brett McLaughlin

نسخه متنی -صفحه : 131/ 45
نمايش فراداده

3.9 Value-Specific Class Bodies

In covering the more advanced features of enums, I can't leave out the ability to define value-specific class bodies. That sounds sort of fancy, but all it means is that each enumerated value within a type can define value-specific methods. This is a rather obscure bit of functionality, but sort of cool to talk about around the water cooler.

3.9.1 How do I do that?

Example 3-9 is an example of a class that determines how the perform( ) method is executed based on the enumerated value. It's a perfect example of value-specific class bodies.

Example 3-9. Value-specific class bodies in an enum
// These are the opcodes that our stack machine can execute.
enum Opcode {
// Push the single operand onto the stack
PUSH(1) {
public void perform(StackMachine machine, int[] operands) {
machine.push(operands[0]);
}
}, // Remember to separate enum values with commas
// Add the top two values on the stack and put the result
ADD(0) {
public void perform(StackMachine machine, int[] operands) {
machine.push(machine.pop( ) + machine.pop( ));
}
},
/* Other opcode values have been omitted for brevity */
// Branch if Equal to Zero
BEZ(1) {
public void perform(StackMachine machine, int[] operands) {
if (machine.pop( ) == 0) machine.setPC(operands[0]);
}
}; // Remember the required semicolon after last enum value
// This is the constructor for the type.
Opcode(int numOperands) { this.numOperands = numOperands; }
int numOperands; // how many integer operands does it expect?
// Each opcode constant must implement this abstract method in a
// value-specific class body to perform the operation it represents.
public abstract void perform(StackMachine machine, int[] operands);
}

NOTE

This example is lifted straight out of Java in a Nutshell, Fifth Edition (O'Reilly).

Skipping past the individual types (which you should already understand), the method that each value should implement is defined: perform( ), which takes two arguments. Finally, each value is followed by an opening curly brace, one or more value-specific methods, and then a closing curly brace. This works in conjunction with any constructor that must be supplied a value, as this enum has. The end result, frankly, is one of the oddest looking Java constructs you'll ever see.

3.9.2 What just happened?

In the lab on Creating an Enum, I mentioned that enumerated type values are created and marked as final (in addition to being public and static), ensuring that they aren't changed by some malicious or unknowing programmer. In the case of a value-specific class body, though, this isn't possible. Instead, an anonymous subclass of the type is created, and the value becomes a singleton instance of that subclass. This still ensures that multiple instances of the same value aren't floating around, but it does change what's going on at the compiler level a bit. Despite this, you still can't extend an enum (see Extending an Enum for more details).

NOTE

I suppose you could really clutter things up with generics and varargs, but you get the idea... value-specific class bodies are often a pain to debug for even mid-level programmers, because of their unusual syntax.

3.9.3 What about...

...just using a more generic method that determines what to do based on a switch statement? Well, that's a better idea, to be honest. Here's the (much cleaner) way to write OpCode:

// These are the the opcodes that our stack machine can execute.
abstract static enum Opcode {
PUSH(1),
ADD(0),
BEZ(1); // Remember the required semicolon after last enum value
int numOperands;
Opcode(int numOperands) { this.numOperands = numOperands; }
public void perform(StackMachine machine, int[] operands) {
switch(this) {
case PUSH: machine.push(operands[0]); break;
case ADD: machine.push(machine.pop( ) + machine.pop( )); break;
case BEZ: if (machine.pop( ) == 0) machine.setPC(operands[0]); break;
default: throw new AssertionError( );
}
}
}

This is so painfully simpler than the first version of OpCode that I hesitated to even include this labbut for completeness, here it is. If at all possible, though, consider using switch in your method bodies to direct program flow, rather than value-specific class bodies.