Java枚举 – 在定义字段之前无法引用该字段

我有一个类似下面的枚举,但是eclipse说每对对方的第一个定义都有错误。

public enum Baz{ yin(yang), //Cannot reference a field before it is defined yang(yin), good(evil), //Cannot reference a field before it is defined evil(good); public final Baz opposite; Baz(Baz opposite){ this.opposite = opposite; } } 

我想要完成的是能够使用Baz.something.opposite来获得Baz.something.opposite相反对象。 这有可能的解决方法吗? 也许在这个例子中定义了yingood之前的yinbad的空占位符?

你可以尝试类似的东西:

 public enum Baz{ yin("yang"), yang("yin"), good("evil"), evil("good"); private String opposite; Baz(String opposite){ this.opposite = opposite; } public Baz getOpposite(){ return Baz.valueOf(opposite); } } 

然后将其引用为

 Baz.something.getOpposite() 

这应该通过它的字符串表示查找枚举值来完成你想要做的事情。 我不认为你能用它来处理Baz的递归引用。

使用switch语句:

 public enum Baz{ yin, yang, good, evil; public Baz getOpposite() { switch (this) { case yin: return yang; case yang: return yin; case good: return evil; case evil: return good; } throw new AssertionError(); } 

或者延迟初始化:

 public enum Baz{ yin, yang, good, evil; public Baz opposite; static { yin.opposite = yang; yang.opposite = yin; good.opposite = evil; evil.opposite = good; } } 

您可能希望将可变字段设为私有并提供getter。

EnumMap怎么样?

 public enum Baz { yin, yang, good, evil; private static final Map opposites = new EnumMap(Baz.class); static { opposites.put(yin, yang); opposites.put(yang, yin); opposites.put(good, evil); opposites.put(evil, good); } public Baz getOpposite() { return opposites.get(this); } } 

您还可以使用抽象方法进行延迟,这比已接受的答案具有类型安全性的好处。

 public enum Baz { yin(new OppositeHolder() { @Override protected Baz getOpposite() { return yang; } }), yang(new OppositeHolder() { @Override protected Baz getOpposite() { return yin; } }), good(new OppositeHolder() { @Override protected Baz getOpposite() { return evil; } }), evil(new OppositeHolder() { @Override protected Baz getOpposite() { return good; } }); private final OppositeHolder oppositeHolder; private Baz(OppositeHolder oppositeHolder) { this.oppositeHolder = oppositeHolder; } protected Baz getOpposite() { return oppositeHolder.getOpposite(); } private abstract static class OppositeHolder { protected abstract Baz getOpposite(); } } 

和测试代码,因为我需要它….

 import org.junit.Test; import static org.junit.Assert.fail; public class BazTest { @Test public void doTest() { for (Baz baz : Baz.values()) { System.out.println("Baz " + baz + " has opposite: " + baz.getOpposite()); if (baz.getOpposite() == null) { fail("Opposite is null"); } } } } 

还有另一种可能的实现(类似于其他一些解决方案,但使用HashMap)。

 import java.util.Map; import java.util.HashMap; public enum Baz { yin, yang, good, evil; private static Map opposites = new HashMap(); static { opposites.put(yin, yang); opposites.put(yang, yin); opposites.put(good, evil); opposites.put(evil, good); } public Baz getOpposite() { return opposites.get(this); } } 

还有一个选择:)使用地图。 它非常冗长,但是这样你只能定义每一对 ,另一个方向是推断的。

 enum Baz { YIN, YANG, GOOD, EVIL; private static final Map opposites = new EnumMap<>(Baz.class); static { opposites.put(YIN, YANG); opposites.put(GOOD, EVIL); for (Entry entry : opposites.entrySet()) { opposites.put(entry.getValue(), entry.getKey()); } } public Baz opposite() { return opposites.get(this); } } 

就个人而言,我最喜欢梅里顿的第二个例子。

然后是完全OTT解决方案。

 public enum Baz { yin, yang, good, evil, right, wrong, black, white; private static class AutoReversingMap> extends EnumMap { public AutoReversingMap(Class keys) { super(keys); } // Make put do both the forward and the reverse. public K put(K key, K value) { super.put(key, value); super.put(value, key); // Better to return null here than a misleading real return of one of the supers. return null; } } private static final Map opposites = new AutoReversingMap(Baz.class); static { // Assume even and odd ones are opposites. for (int i = 0; i < Baz.values().length; i += 2) { opposites.put(Baz.values()[i], Baz.values()[i + 1]); } } public Baz getOpposite() { return opposites.get(this); } } 

多年以后,最短,最苛刻的解决方案

 public enum Baz { YIN, // Use uppercase for enum names! YANG, GOOD, EVIL; public Baz opposite() { return values()[ordinal() ^ 1]; } } 

它依赖于每个成员具有相反的假设,并且它们成对排列。 它希望通过一种方法替换该字段,JVM将优化整个开销。 这在桌面上是合理的,在Android上不太合理。

为了消除开销,我可以在这里使用静态初始化器和许多其他解决方案。