Dynamic enums in Java

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> 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>
        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.

Comments (7)

  1. Andrew Phillips - Reply

    April 4, 2009 at 10:11 am

    Two related links:

    Creating Java Enum Objects at Runtime at TheServerSide and Dynamic Enumerations, revisited (from 2008) which covers much the same ground.

    "There is nothing new in the world except the history you do not know." - Harry Truman

  2. MoffDub - Reply

    April 4, 2009 at 3:40 pm

    Great post and nice use of generics. I wish I had that understanding.

    Assuming no application-side caching, what would be the harm in allowing parts of the backing object to change during the life of the program, as long as you intend those changes to be global? When I used this pattern, the most often-requested change was to change verbage...in your case, the agent's code number.

    The biggest problem I see is knowing when to refresh the dynamic enum, which could be done with some basic parallel programming I suppose.

  3. Andrew Phillips - Reply

    April 6, 2009 at 10:45 pm

    @MoffDub: As regards updates, I can see two general scenarios here.

    Changes to the "backing object" that don't affect its dynamic enum constant would seem to be the easier case. Updating an AgentRecord's licenceToKill property would be an example.

    As this leaves the set of dynamic enum values unaffected, the only thing needed to ensure visibility of such a change is to make sure the actual, current version of the backing object is loaded, rather than a cached version as in the StaticResourceBackedDynamicEnum.
    This might be as easy as modifying the backingValueOf method as such:

    public D backingValueOf(E enumValue) {
      return dynamicEnumerableRepository.loadByName(enumValue.name());
    }
    

    Messing with backing object properties that do affect the enum constants - here, changing an AgentRecord's codeNumber property - is an altogether more tricky affair.

    Since this suddently changes the set of valid enum values, more than a little thought will need to be put into thinking about how to deal with

    • persisted enum constants
    • constants used in the code, e.g. if (agent.equals(new Agent(7))) { ...
    • dynamic enum values in use at the time of the update

    all of which may suddenly be invalid.

    In the second case, a basic recommendation might be: "Avoid, and use agents.valueOf("007") instead, which will at least throw an error if the value no longer exists."
    Apart from that, though, I think any decisions regarding error handling and visibility of updates will have to be case specific.

    As regards when to refresh the dynamic enum, the main use cases we encountered were all based on an external trigger, e.g. a scheduled task or even manual invocation of a JMX method after a release.

    If updates to the set occur more frequently and/or unpredictably than this, an enum-like pattern probably isn't a good fit in the first place...

  4. Andrew Phillips - Reply

    June 21, 2009 at 9:03 pm

    PS: The code for this project is now available at Google Code (project dynamic-enum).

  5. Anonymous - Reply

    August 31, 2011 at 7:21 am

    enum in java is indeed very versatile than just a enumerated type and your post has proved that point. here is good link of various examples of enum in java

  6. pranav - Reply

    January 17, 2012 at 10:59 am

    enum in java is somehow complicated and confusing to understand. But this article explains it clearly. Also would like to share another link enum in java

  7. Gagandeep Singh - Reply

    September 26, 2014 at 11:33 am

    Here is what i did, a sort of hack.

    public static enum Setter {

    DYNAMIC_ENUM_EXAMPLE {

    @Override
    public String setGetValue(String yourValue) {
    return "prefix " + yourValue + " postfix";
    }
    };
    public abstract String setGetValue(String value);
    }

    You can get the value like this :

    Setter.FEE_AGENT_DOESNT_EXIST.setGetValue("namaste")

    Output :

    prefix namaste postfix

Add a Comment