Enhancing Enums

As already mentioned elsewhere, I like Java enums a lot, because you can enhance them easily to make them more useful. But the examples I gave until now are quite complex. So here are some easier ones.

A Very Simple Example: I18n

Often I want to display properties in a human-readable form, and this is often true for enum values. The standard toString() method of an enum will return its value. This is not perfect, especially for foreigners which don’t talk English (the language I’m using for programming). Luckily I already have a system for internationalization (i18n for short). For what we need to know now there is one static method String I18n.getString(String) which takes a string tag and returns an associated string depending on the current Locale (it’s in the de·caff Commons in case you are interested).

Starting from

public enum Test 
{
  One,
  Two,
  Three
}

enhancement is perfectly simple:

public enum Test
{
  One("tagOne"),
  Two("tagTwo"),
  Three("tagThree");
  
  private final String i18nTag;
  
  Test(String tag)
  {
    i18nTag = tag;
  }
  
  public String getDescription()
  {
    return I18n.getString(i18nTag);
  }
}

Of course you’ll have to define the relation between the i18n tags and the localized texts elsewhere, but the above is enough to have a human-readable text for each enum value just by calling its getDescription() method. And in the real world I’d have added some javadoc comments and @NotNull annotations.

Not As Simple: Include Algorithms

If you want to add behavior there is always one step to be done: as enums are final, the individual values cannot have different behavior. But the old programming rule that if something cannot be done introduce an indirection comes to help here. Usually you’ll define or reuse an interface, and give each enum value a different implementation of this interface.

The following example hopefully illustrates why this can be most useful. Assuming you want to handle graphical leader which consist of a text and an arrow which points to a given place. The arrow itself consist of a line and an arrow head. When drawing, you want to allow your users to change the drawing order. So you define the following 6 constants:

public enum LeaderDrawingOrder
{
  HeadLineText,
  HeadTextLine,
  LineHeadText,
  LineTextHead,
  TextHeadLine,
  TextLineHead
}

You’ll probably want to add i18n descriptions as in the first example.

Now for each implementation which draws the leader you’ll have to do something like the following:

switch (drawingOrder) {
  case HeadLineText:
    drawHead();
    drawLine();
    drawText();
    break;
  case HeadTextLine:
    drawHead();
    drawText();
    drawLine();
    break;
  case LineHeadText:
    drawLine();
    drawHead();
    drawText();
    break;
  case LineTextHead:
    drawLine();
    drawText();
    drawHead();
    break;
  case TextHeadLine:
    drawText();
    drawHead();
    drawLine();
    break;
  case TextLineHead:
    drawText();
    drawLine();
    drawHead();
    break;
}

If you are only doing this once, you could bite your way through it, but it is tedious and error-prone.

But as promised there is more generic way. There is no restriction what should be handled so we’ll add a method called order() to the enum which is expected to get three parameters of a any type and return an array of this type where they are correctly ordered:

  public <T> T[] order(T head, T line, T text) 
  {
    // ???
  }

Using a generic method does not restrict the user, and allows easier usage as e.g using java.lang.Object because the returned array has a useful type in the given circumstances.

To fill out the method an indirection is introduced, using a private interface:

public enum LeaderDrawingOrder
{
  HeadLineText(new Sorter() {
    public <T> T[] order(T head, T line, T text)
    {
      return new T[] { head, line, text };
    }
  }),
  HeadTextLine(new Sorter() {
    public <T> T[] order(T head, T line, T text)
    {
      return new T[] { head, text, line };
    }
  }),
  LineHeadText(new Sorter() {
    public <T> T[] order(T head, T line, T text)
    {
      return new T[] { line, head, text };
    }
  }),
  LineTextHead(new Sorter() {
    public <T> T[] order(T head, T line, T text)
    {
      return new T[] { line, text, head };
    }
  }),
  TextHeadLine(new Sorter() {
    public <T> T[] order(T head, T line, T text)
    {
      return new T[] { text, head, line };
    }
  }),
  TextLineHead(new Sorter() {
    public <T> T[] order(T head, T line, T text)
    {
      return new T[] { text, line, head };
    }
  });
  
  private interface Sorter
  {
    <T> T[] order(T head, T line, T text);
  }
  
  private final Sorter sorter;
  
  LeaderDrawingOrder(Sorter sorter)
  {
    this.sorter = sorter;
  }
  
  public <T> T[] order(T head, T line, T text)
  {
    return sorter.order(head, line, text);
  }
}

Adapting the above switch is currently not as nice as could be, but Java 9 will help when it comes out sooner or later:

for (Runnable r : drawingOrder.order(new Runnable() { public void run() { drawHead(); }},
                                     new Runnable() { public void run() { drawLine(); }},
                                     new Runnable() { public void run() { drawText(); }})) {
  r.run();
}

But if you already have items head, line and text which each implement a common interface (a preferable design) it directly looks much better:

for (Drawable d : drawingOrder.order(head, line, text)) {
  d.draw();
}

This is much clearer as the switch statement, and you’ll only have to get the ordering right once when writing the enum. The design is clearer, too, as the ordering is exactly where it belongs: in the place describing it.

N.B.: in some cases it is preferable to use a public (possibly external) interface and make the enum itself implement it. In the above example just making the interface public and the enum implement it would already be enough. But in this case it is not necessary, as the interface is just an implementation detail of no interest to the outside world, so it should stay hidden.