Tuesday, December 18, 2007

Use Enumeration instead of Constant

Using Enumeration instead of Constant

I think we all regularly use integer or String status codes or other magic numbers in our Java code. And we probably also define (static final) constants to make these constants known to the users of our code. While this works fine for small projects, I noticed, when working on larger scale project, that I spent a considerable amount of time trying to find which constants I needed.

In many applications I encounter, this kind of API for a service is quite common:
public static final String DOCUMENT_TYPE_TEXT = "text";
public static final String DOCUMENT_TYPE_SPREADSHEET = "spreadsheet";
List findDocuments(String documentType);
In the above API, it is pretty obvious that I should use the listed constants as the documentType parameter. However, I have seen many cases where the constants are defined in some other place and sometimes an archeology expedition was needed to find the correct values to use. I also have the freedom to pass any String that I didn't get from a constant and just hardcoded. Because of this freedom, the service must check if the passed in documentType parameter is valid (who knows what the user passes to the service).

To make the API more watertight, we could require the user to supply a constant of a class that we define ourselves:
List findDocuments(DocumentType documentType);
This is the DocumentType class:
public class DocumentType {
public static final DocumentType TEXT = new DocumentType("text");
public static final DocumentType SPREADSHEET = new DocumentType("spreadsheet");

private String name;

private DocumentType(String name) {
this.
name= name;
}

public String getName() {
return
name;
}
}

Apache common lang Enum

In the example above we still need to implement the equals() and hashCode() methods to make it possible to check which documentType the user passed to the service. The easy way is to subclass from the Apache common lang Enum class, this superclass implements the boring stuff (equals, hashCode, compareTo, getName):

import org.apache.commons.lang.enums.Enum;
public class DocumentType
extends Enum {
public static final DocumentType TEXT = new DocumentType("text");
public static final DocumentType SPREADSHEET = new DocumentType("spreadsheet");

private DocumentType(String name) {
super(name);
}
}

This solution works well if you're stuck with Java 1.4. However, if you work on a project that runs on Java 5 or later, you should consider the build-in enum types.

Migrate to Java 5 enum

The Java language designers have added the enum type in Java 5 to make it easier to define a type-safe constant. The DocumentType enumeration becomes very simple:

public enum DocumentType {
TEXT,
SPREADSHEET;
}
The code of our service runs unchanged, since the equals method of an enum behaves the same way. In the case you need to interface with legacy code that gets a string from a database or property file to create the corresponding constant, you can add a String as private member of the enum:

public enum DocumentType {
    TEXT("text"),
SPREADSHEET("spreadsheet");

private final String name;

DocumentType(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

Conclusion

Using strong types instead of Strings or integers has these advantages:
  • the interface of your objects is much clearer and less ambiguous,
  • you get code completion in your IDE (no need to "hunt for constants"),
  • easier refactoring by your IDE,
  • type safety - your client cannot supply invalid String or integer constants.