Saturday, August 29, 2015

Enumerated Types - Java

Coming from a long C++ background, one of my favorite constructs in Java was the simple enumerated type. I found the added full-fledged, "class-like" behavioral characteristics useful from the beginning. Just the simple ability to specify a string value in the element constructors alone almost doubles the power of a C++-like enum because it allows you to essentially make String enumerated types when coupled with a toString() method overload. Of course there is much more power to the Java enumerated type's class-like capabilities than just this. One of the first really interesting usages I found for an enum was to create a simple state machine. I no longer have the source code, but if you are interested in how to do this web search for it, there are others who have done this.

When creating API's I find the bounded nature of enums to be self-documenting when used as parameters such as keys in key-value pairs, as opposed to traditional usage of strings. Additionally it removes the possibility of bugs from a consumer of the API making a typo in a string key. However, what if you are releasing an API that has both a defined set of key values and the possibility for unknown key values. I find that sometimes you have the need for an "extensible" enumerated type. For example, in one case we were delivering an Android library for usage in client software with a server-side back-end component that could add functionality over time. I wanted to provide the capability for a customer to use new keys delivered via new server functionality even though the library itself was unaware of these keys at the time of the library creation. However, I still wanted the self-documenting, less error-prone way of specifying keys provided by an enumerated type. To get both behaviors, we introduced a simple construct that we called DynamicEnum. A DynamicEnum is a class, not a Java enum, but uses String constants to enumerate the known values.

public abstract class DynamicEnum {
private final String key;
protected DynamicEnum(String key) {
this.key = key.toUpperCase(Locale.US);
validate(key);
}
private static void validate(String key) {
if (StringUtils.isEmpty(key)) {
throw new IllegalArgumentException("Key should not be null or empty");
}
}
protected final String getKey() {
return key;
}
@Override
public boolean equals(Object o) {
return o != null && o instanceof DynamicEnum && ((DynamicEnum) o).key.equals(key);
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public String toString() {
return key;
}
}
public static final class AppCommandType extends DynamicEnum {
public static final AppCommandType ALARM = new AppCommandType("ALARM");
public static final AppCommandType CALENDAR = new AppCommandType("CALENDAR");
public static final AppCommandType CALLING = new AppCommandType("CALLING");
// etc
/**
* Creates {@link AppCommandType}.
*
* @param key key from server response which is associated with this {@link AppCommandType}.
*/
public AppCommandType(String key) {
super(key.toUpperCase(Locale.US));
}
}
// Later from another class, usage of the key
static{
registerDefaultHandler(AppCommandType.ALARM,AlarmAppHandler.class);
registerDefaultHandler(AppCommandType.ANDROID_INTENT,AndroidIntentAppHandler.class);
registerDefaultHandler(AppCommandType.CALENDAR,CalendarAppHandler.class);
registerDefaultHandler(AppCommandType.CALLING,VoiceDialHandler.class);
//etc
}
//Looks and acts like an enum when used with a known type
The DynamicEnum thus has the benefit of providing a set of values for all known element types, and can also be extended by adding additional values. In an upcoming post on usage of the abstract factory pattern I will describe why this can be useful through a real-world example.

No comments:

Post a Comment