Java intersection types


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.

2 Responses to Java intersection types

  1. Your WordPress template seems to be cutting off the right-hand side of your code examples (under Firefox 19.0 at least).

  2. Paul Murray says:

    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.

Leave a comment