Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 716
durron597 Avatar asked Oct 17 '13 14:10

durron597


People also ask

Are enums immutable?

The types of all fields of the enum are deeply immutable. For example, use ImmutableList and ImmutableSet instead of List and Set . Types are considered immutable if they are primitives, in a set of types that are built in to Error Prone (e.g. java.

Should enum fields be final?

Enum constants are final by definition. But variables referencing a particular constant may be reassigned, unless they're declared final .

How do enums work?

An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.


2 Answers

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());
        }
    }
}
like image 176
robert Avatar answered Oct 21 '22 13:10

robert


As you can see it's not possible due to enum constants are static and you could not initialize A until Z is not initialized.

So this trick should work:

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

  private final String opposite;

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

  public Flippable flip() {
    return valueOf(opposite);
  }
}
like image 40
Admit Avatar answered Oct 21 '22 12:10

Admit