12

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?

4

7 回答 7

16

再一次,也许不像你想要的那么漂亮......

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());
        }
    }
}
于 2013-10-17T14:39:55.377 回答
4

如您所见,这是不可能的,因为枚举常量是静态的,并且在未初始化A之前您Z无法初始化。

所以这个技巧应该有效:

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);
  }
}
于 2013-10-17T14:36:44.153 回答
2

只需映射对立面:

import java.util.*;

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

  private static final Map<Flippable, Flippable> opposites;

  static
  {
    opposites = new EnumMap<Flippable, Flippable>(Flippable.class);
    opposites.put(A, Z);
    opposites.put(B, Y);
    opposites.put(Y, B);
    opposites.put(Z, A);

    // integrity check:
    for (Flippable f : Flippable.values())
    {
      if (f.flip().flip() != f)
      {
        throw new IllegalStateException("Flippable " + f + " inconsistent.");
      }
    }
  }

  public Flippable flip()
  {
    return opposites.get(this);
  }

  public static void main(String[] args)
  {
    System.out.println(Flippable.A.flip());
  }
}

编辑:切换到 EnumMap

于 2013-10-17T14:35:39.953 回答
0

好问题。也许你会喜欢这个解决方案:

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

        private Flippable opposite;

        static {
            final Flippable[] a = Flippable.values();
            final int n = a.length;
            for (int i = 0; i < n; i++)
                a[i].opposite = a[n - i - 1];
        }

        public Flippable flip() {
            return opposite;
        }
    }

    public static void main(final String[] args) {
        for (final Flippable f: Flippable.values()) {
            System.out.println(f + " opposite: " + f.flip());
        }
    }
}

结果:

$ javac Test.java && java Test
A opposite: Z
B opposite: Y
Y opposite: B
Z opposite: A
$ 

如果你想保持实例字段“final”(这当然很好),你可以在运行时索引到数组中:

public class Test {
    public enum Flippable {
        A(3), B(2), Y(1), Z(0);

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

        public Flippable flip() {
            return values()[opposite];
        }
    }

    public static void main(final String[] args) {
        for (final Flippable f: Flippable.values()) {
            System.out.println(f + " opposite: " + f.flip());
        }
    }
}

这也有效。

于 2016-01-20T20:55:14.090 回答
0

下面的代码编译得很好,并且满足了所有 OP 的要求,但是在运行时失败了Exception in thread "main" java.lang.ExceptionInInitializerError. 它留在这里腐烂,作为所有偶然发现它的人不要做的一个例子。

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

private final Flippable opposite;

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

private final Flippable getOpposite(Flippable f) {
    switch (f) {
        case A: return Z;
        case B: return Y;
        case Y: return B;
        case Z: return A;
        default:
            throw new IllegalStateException("Flippable not found.");
    }
}

private void verifyIntegrity() {
    // integrity check:
    Arrays.stream(Flippable.values())
    .forEach(f -> {
        if(!f.flip().flip().equals(f)) {
            throw new IllegalStateException("Flippable " + f + " is inconsistent.");
        }
    });
}

public Flippable flip() {
    return opposite;
}

}

于 2018-03-26T17:00:37.100 回答
0

只要该字段对枚举仍然是私有的,我不确定它的最终状态是否真的值得关注。

也就是说,如果在构造函数中定义了对立面(不一定是!),则条目将所述对立面放入其自己的字段中,同时将自己分配给对立面的字段。这一切都应该通过决赛很容易解决。

于 2017-03-01T13:05:43.097 回答
0

我正在使用这个解决方案:

public enum Flippable {

    A, B, Y, Z;

    public Flippable flip() {
        switch (this) {
            case A:
                return Z;
            case B:
                return Y;
            case Y:
                return B;
            case Z:
                return A;
            default:
                return null;
        }
    }
}

测试:

public class FlippableTest {
    @Test
    public void flip() throws Exception {
        for (Flippable flippable : Flippable.values()) {
            assertNotNull( flippable.flip() ); // ensure all values are mapped.
        }
        assertSame( Flippable.A.flip() , Flippable.Z);
        assertSame( Flippable.B.flip() , Flippable.Y);
        assertSame( Flippable.Y.flip() , Flippable.B);
        assertSame( Flippable.Z.flip() , Flippable.A);
    }
}

这个版本更短,但灵活性有限:

public enum Flippable {

    A, B, Y, Z;

    public Flippable flip() {
            return values()[ values().length - 1 - ordinal() ];
    }

}
于 2017-05-19T06:58:37.413 回答