05 October 2011

Java Enum Interfaces & Enumeration

Java never ceases to amaze me sometimes, especially with some little known tricks that have the potential to help code relatively difficult or tedious tasks in short order. One such problem happens to involve a bean class that contains a large number of fields of different enum types. I found this trick out today while working to figure out how to provide a concrete set of options, but provide some method of enumerating those options in a easy, abstract and consistent way.
For example, I have a Java bean class that holds simple Name/Value pair-type configuration information, but each field has specific options available. Suppose I decided to use an enum for each field so that I could ensure only fields available to the programmer could be used. Now, suppose this enum is a custom implementation, with two fields: a name field used for human-readability and a value field containing the program-specific code. For example:
public enum LightSwitchState {
	ON("Illuminate", true),
	OFF("Off", false);
	private String name;
	private boolean value;
	private LightSwitchState(String name, boolean value) {
		this.name = name;
		this.value = value;
	}
	public String getName() {
		return name;
	}
	public boolean isValue() {
		return value;
	}
}
This is fine and dandy for most things, but as in the original problem where you have a large number of enum-type fields of different types, providing a consistent method of access the name for, say, a menu, special code would have to be written for each type. Now, suppose we introduce the following Java bean designed to hold our Name/Value pair data:
public class NameValuePair {
	public String name;
	public Object value;
	public NameValuePair(String name, Object value) {
		this.name = name;
		this.value = value;
	}
	public String getName() {
		return name;
	}
	public Object getValue() {
		return value;
	}
	//Setters left out for conciseness
}
Suppose now that we want to use this NameValuePair class as the basis for our LightSwitchState:
public enum LightSwitchState {
	ON("Illuminate", true),
	OFF("Off", false);
	private NameValuePair pair;
	private LightSwitchState(String name, boolean value) {
		this.pair = new NameValuePair(name, value);
	}
	public NameValuePair getPair() {
		return pair;
	}
}
Great, now we have a single class we can use, but things still aren't abstracted enough: you still have to call the getPair() method for each enum type that provides it. Therefore, when writing a method that takes in a enum type, we have to specify the enum type by name in order to use the getPair() method. This is a problem now because if we ever had "WidgetSwitchState" which had more states but also had its own getPair() method, we would have to have a method accept the new enum type. But we can take a step further back:
public interface ISwitchEnum {
	public abstract NameValuePair getPair();
}

public enum LightSwitchState implements ISwitchEnum {
	ON("Illuminate", true),
	OFF("Off", false);
	private NameValuePair pair;
	private LightSwitchState(String name, boolean value) {
		this.pair = new NameValuePair(name, value);
	}
	@Override
	public NameValuePair getPair() {
		return pair;
	}
}
Now we have an enum-type that has an interface, and we can create a method that takes an ISwitchEnum value, regardless of the enum-type. But at this point you'll probably say, "Why would you go through all this? By taking an ISwitchEnum as a parameter, you lose the ability to determine if the parameter is a proper value and the whole reason for using an enum is lost." Well, this is where the context of why you're using this method comes into play. In my case, I have an abstract classes that displays a UI to the user, where each field in the UI is a drop-down combo box with the valid values for that field. So how do I do fill out these drop-down combo boxes without tons of hard coding? The trick above allows us to do something like this:
public void printOptionsForType(Class type) {
	ISwitchEnum[] enumConstants = type.getEnumConstants();
	for(ISwitchEnum v : enums) {
		System.out.format(">%s\n", v.getPair().getName());
	}
}
However, when I actually use the fields, I use the enum type name rather than the interface, ensuring that the methods that require specific values are guaranteed by the compiler to be within a predefined set of values, but allowing generic things like UI population of the combo box to happen with minimal code. Pretty nifty, eh?

1 comment:

Sumantha said...

Nice Article