Search code examples
javaenumsself-reference

Self referential enum with immutable parameters


Consider the following sscce

public enum Flippable 
  A (Z), B (Y), Y (B), Z (A);

  private final Flippable opposite;

  private Flippable(Flippable opposite) {
    this.opposite = opposite;
  }

  public Flippable flip() {
    return opposite;
  }
}

This doesn't compile, because Z and Y haven't been declared to be allowed to be arguments of A and B's constructor.

Potential solution 1: Hardcoded Methods

public enum Flippable {
  A {
    public Flippable flip() { return Z; }
  }, B {
    public Flippable flip() { return Y; }
  }, Y {
    public Flippable flip() { return B; }
  }, Z {
    public Flippable flip() { return A; }
  };
  public abstract Flippable flip();
}

While functional, this seems stylistically quite gross. Though I can't put a finger on why this would be a real problem.

Potential solution 2: static loading

public enum Flippable {
  A, B, Y, Z;

  private Flippable opposite;

  static {
    for(Flippable f : Flippable.values()) {
      switch(f) {
      case A:
        f.opposite = Z;
        break;
      case B:
        f.opposite = Y;
        break;
      case Y:
        f.opposite = B;
        break;
      case Z:
        f.opposite = A;
        break;
      }
    }
  }

  public Flippable flip() {
    return opposite;
  }
}

This is even more gross than the first solution, as the field is no longer final, and is vulnerable to reflection. Ultimately that is an obscure worry, but suggests a bad code smell.

Is there a way to do this that is essentially the same as the first example, but compiles properly?


Solution

  • Again perhaps not as pretty as you were looking for ...

    public enum Flippable {
        A, B, Z, Y;
    
        static {
            A.opposite = Z;
            B.opposite = Y;
            Y.opposite = B;
            Z.opposite = A;
        }
    
        public Flippable flip() {
            return opposite;
        }
    
        private Flippable opposite;
    
        public static void main(String[] args) {         
            for(Flippable f : Flippable.values()) {
                System.out.println(f + " flips to " + f.flip());
            }
        }
    }