Java Enums can implement Interfaces

Something which came as a surprise to me but ended up quite useful, was that enums in Java can also implement interfaces.

A typical enumeration in Java, introduced in Java 5, looks like a String value, but also introduces other features which make them a particularly useful language feature. Besides looking like an typical enumeration with a finite ordered list of values, it is also possible to attach additional information to each enumeration value.

A basic enumeration, which defines the four suits in a typical set of cards looks like this:

public enum Suit 
{ CLUBS, DIAMONDS, HEARTS, SPADES }

To attach additional information to the enumerated type, add a private constructor, add a private field, and get each enumeration value to call the new constructor.

public enum Numbers {
	One(1), Two(2), Three(3);

	private Numbers(int value)
	{
		this.value = value;
	}
	
	public int getValue()
	{
		return this.value;
	}
	
	private int value;
}

In the example above, we also introduced the getValue() method. This introduces the concept of behaviour to our enumeration; not something we would normally consider. It also lends itself to the idea of common behaviour between enumerated types.

To show how this works, consider an interface which defines the mapping between a class and a table.

public interface DatabaseMapping {
	public String getType();
	public boolean isKey();
	public boolean isNullable();
	//public String name();
}

The method name() is commented for now, we’ll get back to that in a minute.

To use this, we can now create a new Java class and then define the mapping of that class to a database.

public class Person {
	public String getFirstname() {
		return firstname;
	}
	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}
	public String getLastname() {
		return lastname;
	}
	public void setLastname(String lastname) {
		this.lastname = lastname;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	private String firstname;
	private String lastname;
	private int age;
}

(I realise there are better ways to map a class to a database table, this example is contrived) When we define the mapping from our class to our table, we can define a separate mapping class. We create this class, we will make it an enumeration and allow each class/database field to be a member in an enumeration.

public enum PersonMapping {
	personId, firstname, lastname, age;
}

Using the trick where we are able to attach additional information to enumerated types, we define some of the database mapping details:

public enum PersonMapping {
	personId("INTEGER", true, false), 
	firstname("TEXT", false, false), 
	lastname("TEXT", false, false),
	age("INTEGER", false, false);
	private PersonMapping(String type, boolean key, boolean nullable )
	{
		this.type = type;
		this.key = key;
		this.nullable = nullable;
	}
	public String getType() {
		return type;
	}
	public boolean isKey() {
		return key;
	}
	public boolean isNullable() {
		return nullable;
	}
	private final String type;
	private final boolean key;
	private final boolean nullable;
}

So now we are able to use the enumeration to iterate the fields and do some database stuff. For the moment we’ll hold off including the interface.

public String dbTableCreate(String tableName) {
	StringBuilder builder = new StringBuilder("Create table ");
	builder.append(tableName);
	builder.append("(");
	for (PersonMapping column : PersonMapping.values()) {
		builder.append(column.name());
		builder.append(" ");
		builder.append(column.getType());
		builder.append(column.isKey() ? " primary key" : "");
		builder.append(", ");
	}
	builder = new StringBuilder(builder.substring(0, builder.length() - 2));
	builder.append(");");
	return builder.toString();
}

Which looks fine for our PersonMapping enumerated type, but doesn’t allow us to plug in different class-to-database mappings. Time to introduce the interface. Looking closely at the code above, we need to use two of the additional methods available to all enumerations, name() and values(). The first returns the text name of the current enumerated value, and the second allows us to access the values that make up this enumerated type.

First add the interface to the enumeration:

public enum PersonMapping implements MappingEnum{

Uncomment the name() method so that it can be accessed from the interface.

public interface MappingEnum {
	public String getType();
	public boolean isKey();
	public boolean isNullable();
	public String name();
}

Final piece of the puzzle is to write the dbTableCreate() above to allow it to be reused with other enumerated types. The problem we need to work around is that the values() method is static, making it difficult to pass the enumerated type to the method. The simple solution is to call the method explicitly and pass the values:

dbTableCreate("tableName", PersonMapping.values()));

… and realise that we can now receive the values using the interface …

public String dbTableCreate(String tableName, MappingEnum[] values) {
	StringBuilder builder = new StringBuilder("Create table ");
	builder.append(tableName);
	builder.append("(");
	for (MappingEnum column : values) {
		builder.append(column.name());
		builder.append(" ");
		builder.append(column.getType());
		builder.append(column.isKey() ? " primary key" : "");
		builder.append(", ");
	}
	builder = new StringBuilder(builder.substring(0, builder.length() - 2));
	builder.append(");");
	return builder.toString();
}

Conclusion

* Enumerations can store additional data and have additional methods,
* Enumerations can use interfaces to define behaviour,
* Enumerated item behaviour can be accessed via an interface, the same as regular Java classes,
* The values() method from enumerations can be used to return an array of a interface.

Combined, it allows us to treat enumerated types as both regular Java classes and enumerated types and interact using common behaviour rather than being tied to a single implementation.