Pure Danger Tech


navigation
home

Putting extra state on enum constants

03 Jan 2007

I had occasion the other day to push some extra state onto my enum constants. I hadn’t done this before and it took me a while to find any good docs on it on the web.

In my case I had some classes in the Strategy pattern and I wanted to create an enumerated type for the different strategies. Instead of making a map of enum value to strategy instance, I just pushed the instances into the enum values themselves.

Here’s an example with some helpful comments: [source:java]

public enum FightingStrategyType {</p>

// Declare each of the enum constants – comma-delimited,

// terminate list with a semi-colon. After each constant, pass

// any parameters that need to be sent to a constructor. Here

// we pass the strategy instance for each enumerated value.

KUNG_FU (new KungFuStrategy()),

GIRLIE_SLAP (new GirlieSlapStrategy()),

MORTAL_KOMBAT (new MortalKombatStrategy()),

RUN_AWAY (new RunAwayStrategy());

// Internal attribute for each enum value

private FightingStrategy strategy;

// Constructor for the enum value that takes an extra attribute.

// Constructors cannot be called explicitly in an enum type

// so any constructor should be declared “private” to make this clear.

private FightingStrategyType(FightingStrategy strategy) {

this.strategy = strategy;

}

// Getter method to retrieve the strategy for an enum value

public FightingStrategy getStrategy() {

return this.strategy;

}

}

[/source]

Once you have defined this enumerated type, you can use the enumerated values as expected but you can call getStrategy() on them to retrieve the actual strategy instance. [source:java]

public class Person {

private FightingStrategyType strategyType;

public Person(FightingStrategyType strategyType) {

this.strategyType = strategyType;

}

public void attacked(Attacker attacker) {

// Get the strategy from the enumerated value itself

FightingStrategy strategy = strategyType.getStrategy();

strategy.fight(attacker);

}

}

Person person = new Person(FightingStrategyType.KUNG_FU);

Bear bear = new Bear();

person.attacked(bear);

[/source]

I’m not sure this is a very compelling example of usage but it would be difficult to show enough code to explain why this was worthwhile in my case. The alternative here is of course to just say “new Person(new KungFuStrategy())” and skip the whole enumerated type entirely. In my case, I’m starting from an inherently string-based API and I want to start from the name of the enumerated type (and hide the name of the actual class related to the type). The built-in name() and valueOf() methods in enum make this easy.

One case where this is more obviously useful is if the enumerated values have some real constant attributes that correspond to each enumerated value. In that case, it’s a big win to create those directly on the enumerated type. Check out the Planet example in Sun’s docs on enums for a good use case on this.

Another interesting idea is that the state on the enum values does not have to be constant – each value is an instance that can have mutable state (via getter/setters). Seems like there would only be some rare cases where you’d want enumerated values with variable state but it’s an interesting idea. Something like: [source:java]

public enum ShirtSize {

S, M, L, XL;

private int price; // in dollars

public int getPrice() { return this.price; }

public void setPrice(int dollars) { this.price = dollars; }

}

ShirtSize.S.setPrice(3);

ShirtSize.M.setPrice(4);

ShirtSize.L.setPrice(5);

ShirtSize.XL.setPrice(10);

for(ShirtSize size : ShirtSize.values()) {

System.out.println(size.name() + “: ” + size.getPrice());

}

[/source]

This would let you store the price per size and change it dynamically as needed.

Gotta love that powerful little enum!