Blog

Dynamic enums in Java

02 Apr, 2009
Xebia Background Header Wave

The introduction of Enums in Java 5 finally provided a powerful (and slightly magical) way of modelling fixed sets. One can have too much of a good thing, however, and in some situations an enum is a little too fixed to be convenient.
Here, I’d like to discuss a pattern I’ll call – for want of a better name – the dynamic enum, that addresses this issue.
"’Dynamic enums’? Isn’t that a bit like ‘type-safe scripting language’ or ‘fully-specified requirements’?" Well, let me try to outline the intended purpose of a dynamic enum:
Assume the set of countries of the world (all 203 of them, at the time of writing) are part of your domain model. Clearly, the number of countries is limited, so String is not really a good choice.
What about an enum? Well, the list of countries isn’t that fixed, either – every time you saw a news item about a country being invaded, or declaring independence, you’d be thinking: "Must remember to go and recompile that program tomorrow and request an emergency release."
One common-sense solution is to maintain a list of countries (e.g. in a database table or resource file), and load that into memory when the application starts. Now, adding a country merely requires an application restart. But how to check if a given String or – depending on your modelling choices – Country instance actually represents one of the countries? Well, some kind of

CountryRepository.get(countryIsoCode) != null

check could be used, but that’s entirely application-specific logic.
A different scenario: assume you are building an HR application for Her Majesty’s Secret Service. One of the key elements of the domain model will be your AgentRecord, which might include things like code number, name, licence to kill, number of kills, total value of damaged property etc.
These are pretty heavyweight objects to be lugging around – you probably wouldn’t want Moneypenny to have to choose from a drop-down list of AgentRecords. Ideally, we’d like some kind of "lightweight representation" that is in 1-1 correspondence with the AgentRecords.
Now there is one such "natural" representation: the code number. But if we use that in our code, we will probably end up with things like

BureaucracyService.recordDamageCaused(int agentCodeNumber, int damageInMillionGBP)

which is certainly less self-documenting than

BureaucracyService.recordDamageCaused(Agent agent, int damageInMillionGBP)

Here, Agent is an "enum-like" object that might look like

public class Agent {
  public final int codeNumber;
  public Agent(int codeNumber) {
    this.codeNumber = codeNumber;
  }
}

or you could add a getter if you don’t like the idea of a public final field.
"But wait a minute! What’s to stop you creating an agent with a non-existent code number? Worse, what if your business logic uses new Country("Soviet Union") somewhere? If you were using Countries.SOVIET_UNION and then remove the enum constant, you’d get a compile error. But now you’re simply left with a timebomb in your code!"
Very true. That just seems to be some of the price you have to pay for your lunch. Possible approaches to mitigate this risk include keeping usage of specific enum constants to a minimum (not always feasible), or deciding that items may only be added to your dynamic enum, not removed.
In any case, dynamic enums appear to be less relevant for systems in which enum constants are not chosen "dynamically", i.e. on the basis of user input. Compare

class TrafficLight {
  private static enum State { RED, GREEN }
  private State state;
  void changeState(boolean goGreen) {
    state = (goGreen ? State.GREEN : State.RED);
  }
}

a "static choice" example, with

class TrafficLight {
  ...
  void changeState(String targetState) {
    state = State.valueOf(targetState);
  }
}

which demonstrates what I mean by "dynamic" choice. In this latter case, if targetState is untrusted (e.g. comes from an HTTP request submitted by a user interface), there is always a chance that the value is invalid. Using a "real" enum does not change that!.
If State now were a dynamic rather than a real enum (allowing me to add AMBER without having to recompile, for instance), wouldn’t it be convenient if I could call valueOf, values and all the other methods we’re used to with enums, and have them behave the same way?
To me, that certainly seems preferable than having to rewrite the code to call stateRepository.find(targetState), or any other similarly non-standard method.
This is where DynamicEnum and DynamicEnumerable come in. A heavyweight object like AgentRecord would implement DynamicEnumerable<Agent> to indicate that the set of agent records is essentially fixed and can be represented by a set of lightweight Agent objects.
Because Java enums associate each enum constant with a String value, a heavyweight DynamicEnumerable also needs to define a String name associated to its lightweight representation.

public interface DynamicEnumerable {
    String name();
    E enumValue();
}
public AgentRecord implements DynamicEnumerable {
    int codeNumber;
    String name;
    boolean licenceToKill;
    int numKills;
    long totalDamagedPropertyValue; // in GBP * 100
    public String name() {
        return "00" + Integer.toString(codeNumber);
    }
    public Agent enumValue() {
        return new Agent(codeNumber);
    }
}

The DynamicEnum itself is essentially a factory for the dynamic enum constants that represent a given DynamicEnumerable. It provides all the functionality that is available via the static methods of a regular enum, such as values.

public interface DynamicEnum<e, D extends DynamicEnumerable> extends Comparator {
    boolean exists(E enumValue);
    E valueOf(String name);
    List values();
    int ordinal(E enumValue);
    Set range(E from, E to);
    D backingValueOf(E enumValue);
}

In addition, the backingValueOf method provides a way of accessing the "backing" heavyweight object corresponding to a dynamic enum constant. This could be required, for instance, to populate an "Agent details" screen.
Because the practicalities of retrieving, and rules for ordering and updating, the set of backing objects are entirely implementation-specific, DynamicEnum is an interface, rather than a parent class. One consequence is that, unlike in the case of a regular enum, values etc. aren’t static methods on the lightweight class.
In principle, the lightweight class could implement DynamicEnum itself, but with all the additional (instance!) methods, and the code to implement them, it would hardly be very lightweight anymore.
Instead, anticipated usage is likely to comprise a simple, usually immutable object such as Agent as the lightweight object, and an Agents repository or factory that implements DynamicEnum<Agent, AgentRecord> and is responsible for loading AgentRecords and maintaining the correspondence between Agents and their backing AgentRecords.
Perhaps the most basic, but by no means uncommon scenario is a dynamic enum whose members are defined in some static resource or database table. This is loaded at application startup on the assumption that it will not change while the application is running. Changing the enum thus requires an application restart, but that is often still much more convenient than a recompile and redeploy.
Here is a simple DynamicEnum implementation that handles this case. It uses a repository to load the backing objects from a given resource; the order in which the repository returns the items determines the enum ordering.

public interface DynamicEnumerableRepository {
    List loadAll();
}
public class StaticResourceBackedDynamicEnum<e, D extends DynamicEnumerable>
        implements DynamicEnum {
    private final List orderedDynamicEnumValues;
    private final Map dynamicEnumValues;
    private final Map dynamicEnumValueNames;
    private class DynamicEnumValueDescriptor {
        private int ordinal;
        private D backingObject;
        private DynamicEnumValueDescriptor(int ordinal, D backingObject) {
            this.ordinal = ordinal;
            this.backingObject = backingObject;
        }
    }
    public StaticResourceBackedDynamicEnum(
            DynamicEnumerableRepository dynamicEnumerableRepository) {
        List dynamicEnumerables = dynamicEnumerableRepository.loadAll();
        int numDynamicEnumerables = dynamicEnumerables.size();
        orderedDynamicEnumValues = new ArrayList(numDynamicEnumerables);
        dynamicEnumValues = new HashMap(numDynamicEnumerables);
        dynamicEnumValueNames = new HashMap(numDynamicEnumerables);
        for (int i = 0; i < numDynamicEnumerbles; i++) {
            D dynamicEnumerable = dynamicEnumerables.get(i);
            E dynamicEnumValue = dynamicEnumerable.enumValue();
            // ...
            orderedDynamicEnumValues.add(dynamicEnumValue);
            dynamicEnumValues.put(dynamicEnumValue,
                    new DynamicEnumValueDescriptor(i, dynamicEnumerable));
            dynamicEnumValueNames.put(dynamicEnumerable.name(), dynamicEnumValue);
        }
    }
    public boolean exists(E enumValue) {
        return dynamicEnumValues.containsKey(enumValue);
    }
    public E valueOf(String name) {
        // ...
        return dynamicEnumValueNames.get(name);
    }
    // ...
    public List values() {
        return new ArrayList(orderedDynamicEnumValues);
    }
    public int ordinal(E enumValue) {
        // ...
        return dynamicEnumValues.get(enumValue).ordinal;
    }
    public int compare(E enumValue1, E enumValue2) {
        // ...
        return (ordinal(enumValue1) - ordinal(enumValue2));
    }
    public Set range(E from, E to) {
        // ...
        // subList treats the "to" index as *exclusive*, but *inclusive* is required here
        return new HashSet(orderedDynamicEnumValues.subList(
                orderedDynamicEnumValues.indexOf(from), orderedDynamicEnumValues.indexOf(to) + 1));
    }
    public D backingValueOf(E enumValue) {
        // ...
        return dynamicEnumValues.get(enumValue).backingObject;
    }
}

Most of the argument checking has been omitted from the code snippet. The implementation isn’t particularly efficient, either – there is plenty of duplication in the three main internal data structures orderedDynamicEnumValues, dynamicEnumValues and dynamicEnumValueNames that no doubt could be optimised.
The code also expects the underlying repository to be usuable in the constructor. If the repository requires a transaction to access a database this may well not be the case, for instance when using Spring’s declarative transaction management.
An Initialization on Demand Holder, or any other suitable singleton initialization pattern, could be used here.
In any case, assuming that there is an AgentRecordRepository capable of reading all the AgentRecords from Moneypenny’s old filing cabinet, creating an Agents dynamic enum is as easy as

public class Agents extends StaticResourceBackedDynamicEnum {
    public Agents(DynamicEnumerableRepository agentRecordRepository) {
        super(agentRecordRepository);
    }
}

This enables you to write code such as:

Agents agents = new Agents(agentRecordRepository);
assert agents.valueOf("007").equals(new Agent(7)) // assuming Agent defines equals on code number
assert agents.values().contains(new Agent(8)); // 008 ('Bill') appears in Goldfinger
assert (agents.exists(new Agent(-73)) == false);
assert (agents.range(new Agent(1), new Agent(7)).size() == 5); // 001, 004, 005, 006 and 007
assert agents.backingValueOf(new Agent(7)).hasLicenseToKill();

The source code, and a Maven project, are available here.

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts