There is an obscure little language feature in java, called intersection types, which I have miraculously found a use for. Quite a nice use really.
As you no doubt know, enumerated types cannot be subclassed, because they “belong” to the language itself. This is a bit of a hassle when you have a variety of enumerated types that have common functionality. A typical example of this is when your enum constants have corresponding database records.
(BTW: this is always a horror to work with, because you need to synchronise your java code with your database records)
You have a “lookup” table in the database, containing sets of constants for “state” and for “city”, let’s say, and an “importance” field. The corresponding java is:
enum City { NSW, ACT, VIC; private int importance; public int getImportance() {...;} void setImportance(); } enum State { SYD, CAN, WAG; private int importance; public int getImportance() {...;} void setImportance(); }
Now, setting the importance from the database is reasonably easy:
for(City c: Enum.allOf(City.class)) { c.setImportance(getImportanceFor(c)); }
But you need a separate loop for each enumeration. There may be several. Even worse, the getImportanceFor()
method is not type-safe: it just takes an enum.
A java intersection type is a type that is an intersection of othertypes. Just as an interface can extend any number of other interfaces, an intersection type can “extend” a class and any number of interfaces. But it’s a synthetic thing: no class is created for it, there is no .class
file. The compiled code simply understands that the class extends the correct types, without casting or type-checking.
So. Let’s create an interface and implement it:
interface Lookup<T extends Enum<T>> { public int getImportance(); public void setImportance(int importance); } enum City implements Lookup<City>{ NSW, ACT, VIC; private int importance; public int getImportance() { return importance; } public void setImportance(int importance){ this.importance=importance; } } enum State implements Lookup<State>{ SYD, CAN, WAG; private int importance; public int getImportance() { return importance; } public void setImportance(int importance){ this.importance=importance; } }
At this stage, we can do the magic thing: we can state that a method takes a parameter that is both an Enum
and also a Lookup
. So, let’s define a method to which you pass a lookup/enum class, and which fills in the importance values:
enum State implements Lookup<State>{ NSW, ACT, VIC; static { LookupDAO.getDAO() .fillinImportance(State.class); } private int importance; public int getImportance() { return importance; } public void setImportance(int importance) { this.importance=importance; } } enum City implements Lookup<City>{ SYD, CAN, WAG; static { LookupDAO.getDAO() .fillinImportance(City.class); } private int importance; public int getImportance() { return importance; } public void setImportance(int importance) { this.importance=importance; } } abstract class LookupDAO { public <T extends Enum<T> & Lookup<T>> void fillinImportance(Class<T> c) { for(T e: EnumSet.allOf(c)) { e.setImportance( getLookupImportance(e.name())); } } // DAO implementation goes in a subclass //////// protected abstract int getLookupImportance(String code); public static LookupDAO getDAO() { throw new UnsupportedOperationException( "Haven't written this bit"; ); } }
as you can see, the argument c
of fillinImportance
is declare to be both an enum and a Lookup. Our code freely uses code specific to Enum types: allOf()
, name()
, and also code specific to Lookup types: setImportance()
.
The important thing is: it’s all type-safe. Aside from the declaration itself, the code looks simple and everything does what it’s supposed to do.
Your WordPress template seems to be cutting off the right-hand side of your code examples (under Firefox 19.0 at least).
Looks ok to me (obviously, or I would have done something about it by now). Firefox 19.0.2, on OSX. Only way I can make it clip is to drag the window narrow.
But I’ll put in some line breaks anyway.
—
There you go. Some linebreaks. Using a static initializer is bad of course because there might not be a database connection at that stage, but that’s not the point of this snippet. Also, it’s probably not a good idea to paramaterise the Lookup interface, but meh.