Forward Usage of Enum Siblings

Sometimes you want to access other values of the same enum even if they are not yet defined. Is this possible?


Frequent readers know that I’m a big fan of Java Enums. But I had a problem for which there was no satisfying solution. Assume I want to define a simple enum:

 1public enum Direction
 3  Forward(Backward),
 4  Backward(Forward);
 6  private final Direction opposite;
 8  Direction(Direction opposite)
 9  {
10    this.opposite = opposite;
11  }
13  public Direction getOpposite()
14  {
15    return opposite;
16  }

When I have a direction, I want to a have a simple way of accessing its opposite. But the above code will not compile, because in line 3 there is a forward reference to the not yet defined enum value Backward.

Standard Solution

The standard solution for this kind of problem is to calculate the opposite when requested:

public enum Direction

  public Direction getOpposite()
    switch (this) {
    case Forward:
      return Backward;
    case Backward:
      return Forward;
      throw new RuntimeException("Never should reach here!");

To me this is annoyingly unsatisfying, as a basically constant value is calculated each time it is requested. By the way the same is true for the standard Enum.values() method which calculates a new value array on each request. See my post here for a possible improvement.

I’ve seen lazy initialization used here, too, where the opposite field from the first code example is set in this method when it is null. But this creates other problems because in general it would require synchronization and at least a condition test on each request.

Better Solution

I’d consider the following a better solution:

 1public enum Direction
 3  Forward,
 4  Backward;
 6  static {
 7    Forward.opposite = Backward;
 8    Backward.opposite = Forward;
 9  }
11  private Direction opposite;
13  @NotNull
14  public Direction getOpposite()
15  {
16    assert opposite != null;
17    return opposite;
18  }

Here a static initializer is used, so calculating the value is done once when the enum is generated, just as it should.

There are remaining problems, but they are not too hard:

  • You cannot make the field public final (this would allow to do without the getter method), as it is not final from the compiler’s point of view.
  • You’ll have to remember to update the static initializer whenever you add values to your enum. But that is basically the same with the standard solution.