Customizing Enums

Enumerated types (enums) are a way to define fixed set of constants, so helpful in many areas of software development. In the most common case, in your Java code written for Java 5 or newer, you will use enums for int constants and replace chunks of code that look like this

    public static final int DIRECTION_NORTH     = 0;
    public static final int DIRECTION_SOUTH     = 1;
    public static final int DIRECTION_EAST      = 2;
    public static final int DIRECTION_WEST      = 3;

with something like this

public enum Direction {
    NORTH, SOUTH, EAST, WEST;
}

Then, you’ll change method definitions that look like

public void changeDirection(int direction) {
    // do something
}

with something like this

 public void changeDirection(Direction direction) {
    // do something
}

And finally, the method call will be changed too, from

ship.changeDirection(DIRECTION_EAST);

to

ship.changeDirection(Direction.NORTH);

There are a few obvious benefits from using enums over the standard int constants, such as the type safety and namespaces for example.

But we can also use enums to define other types of constants, string constants for example. You can often see examples of using enums to make a switch idiom for strings possible in Java. Take a look at the following example

        String direction = "WEST";
        switch (Direction.valueOf(direction)) {
            case WEST :  System.out.println("Go west!");
                               break;
            case EAST :  System.out.println("Go east!");
                               break;
            default : System.out.println("Go somewhere!");
        }

It prints Go west! as a result which is great.

But in order to be truly useful for string constants purpose, enums needs some extra tuning and here’s why.

As the JavaDoc of the valueOf method used above says:

The name must match exactly an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)

So if you want your switch work well you need to be sure that you’ve passed a string with the value exact to some of the identifiers defined in the enum. So if you change your direction variable value to west, you’ll get the following exception

Exception in thread "main" java.lang.IllegalArgumentException:
    No enum const class net.scriptinginjava.test.EnumTest$Direction.west
    at java.lang.Enum.valueOf(Enum.java:192)
    at net.scriptinginjava.test.EnumTest$Direction.valueOf(EnumTest.java:1)
    at net.scriptinginjava.test.EnumTest.main(EnumTest.java:28)

which could be tricky in some situations.

Another scenario where enums could benefit from extra customization is when you have to have a kind of string mapping between identifiers and their values. Take for example that you want to deal with HTTP headers in this way. You cannot use names like if-modified-since as an enum identifier since it is not a regular Java variable name so some kind of transformation is needed and the question is, what can we do to customize enums?

As we have seen earlier enums extend java.lang.Enum class so we can start looking there for enhancements we need. In order to customize represenation of strings in our enum, we need to pay attention to two methods:

  • toString – which prints the value of a constant. By default, this method returns the exact name of the constant identifier, just as the name method.
  • valueOf – which returns a enum value from a string. Unfortunately, this method cannot be overridden so we have to find a workaround for converting custom strings to enum values without using valueOf method.

Now, take a look at the following enum declaration

    public enum HttpHeader {
        IF_MODIFIED_SINCE, USER_AGENT, UNKNOWN;

        public String toString() {
            return name().replaceAll("_", "-").toLowerCase();
        }

        public static HttpHeader getValue(String value) {
            try {
                return valueOf(value.replaceAll("-", "_").toUpperCase());
            } catch (Exception e) {
                return UNKNOWN;
            }
        }
    }

The first thing to notice is that we defined uppercased values with “_” char as a word separator. It’s kind of a “good practice” so let it be. Now take a look at the toString method, which replaces _ with in the name and returns such modified value. So, the following line

System.out.println(HttpHeader.IF_MODIFIED_SINCE);

will print if-modified-since value.

Now let’s see how to use the switch statement with this enum. Take a look at this example

     String[] headers = new String[]{"if-modified-since", "User-Agent", "test"};
            for (String header : headers) {
                switch (HttpHeader.getValue(header)) {
                case IF_MODIFIED_SINCE  :
                     System.out.println("if-modified-since header found");
                     break;
                case USER_AGENT            :
                     System.out.println("user-agent header found");
                     break;
                case UNKNOWN                :
                default                            :
                     System.out.println("unkown header found: " + header);
            }
     }

It will print the following output

if-modified-since header found
user-agent header found
unkown header found: test

As you can see, this enum has a greater flexibility in matching strings to enum constants. Also, you can note that now we can gracefully fail if the passed string does not match any value. It returns the UNKNOWN value instead of throwing an exception. Of course, whether this is a desirable funcionality depends on your requirements and design.

In the previous example, we used a simple transformation in the toString method to customize the value of the printed constant. In case that you have needs for more complex transformation you can apply another technique. Take a look at this example:

    public enum HttpHeader {

        IF_MODIFIED_SINCE("If-Modified-Since"),
        USER_AGENT("User-Agent"),
        UNKNOWN("");
        private String realName;

        private HttpHeader(String realName) {
            this.realName = realName;
        }

        public String toString() {
            return realName;
        }

        public static HttpHeader getValue(String value) {
            try {
                return valueOf(value.replaceAll("-", "_").toUpperCase());
            } catch (Exception e) {
                return UNKNOWN;
            }
        }
    }

This enum definition defines a constructor with a string parameter. We can use this parameter to define a string representation of the constant. So now, the code snippet

System.out.println(HttpHeader.IF_MODIFIED_SINCE);

will print the following output

If-Modified-Since

Everything else remains the same as in our previous examples.

Customized like this, enums could be used for dealing with string constants, which could be helpful in processing header names (and values) for example.