Wrapping the constant interface anti pattern into an Enum
Especially in legacy code, we sometimes come across code like this, also known as the Constant Interface Antipattern:
public interface Constants { static int DML_DELETE = 0x02; static int DML_INSERT = 0x05; static int DML_UPDATE = 0x10; }
Since Java 5, a much better alternative is to use an enum. If possible, code like the above which only defines constants should be converted into a corresponding enum. However, sometimes this is not possible, for example when using a library where the source code is not available. Then, we can still take advantage of enums in our own code by wrapping the legacy constants into an enum type. By default, a Java enum only allows to map from the the enum name to the enum itself, but in our scenario we also need to be able to map the legacy integer constants to the corresponding enum. The following code shows how the above interface could be wrapped into an enum:
public enum ConstEnum { DML_DELETE(Constants.DML_DELETE), DML_INSERT(Constants.DML_INSERT), DML_UPDATE(Constants.DML_UPDATE); private int constValue; // we need to create a reverse map for the enum in order to allow reverse lookups private static HashMap<Integer, ConstEnum> revMap = new HashMap<>(); static { for (ConstEnum e : values()) { revMap.put(e.value(), e); } } // private constructor for the integer value private ConstEnum(int someValue) { constValue = someValue; } public int value() { return constValue; } public static ConstEnum enumFor(int value) { ConstEnum result = revMap.get(value); if (result == null) { throw new NoSuchElementException(ConstEnum.class.getName() + " for " + value); } return result; } }
The core of this code is the revMap hashmap which is initialized in the static initializer and puts all available enum objects and their corresponding integer values into a hashmap, so that we can look up the enum object by its integer value, which is done in the enumFor() method. The static values() method is provided automatically by the java compiler for all enum types. The following samples show how the enum type can now be used: List all available values:
for (ConstEnum val : ConstEnum.values()) { System.err.println(val + "=" + val.value()); }
DML_DELETE=2 DML_INSERT=5 DML_UPDATE=16
Convert legacy int value into enum:
int someValue = 5; ConstEnum e = ConstEnum.enumFor(someValue); System.err.println(e + "=" + e.value());
DML_INSERT=5
Switch on a legacy int value:
someValue = 2; ConstEnum e = ConstEnum.enumFor(someValue); switch(e) { case DML_DELETE : System.err.println("DELETE"); break; case DML_INSERT : System.err.println("INSERT"); break; case DML_UPDATE : System.err.println("UPDATE"); break; }
DELETE