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

Java枚举-在定义字段之前无法引用该字段,java,enums,Java,Enums,我有一个类似下面的枚举,但是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(B

我有一个类似下面的枚举,但是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.contract
获得
Baz.something
的相反对象。是否有可能的解决方法?在本例中定义了
yin
good
之前,可能为
yang
bad
设置了一个空占位符?

您可以尝试以下操作:

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.

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

import java.util.Map;
导入java.util.HashMap;
公共图书馆{
殷,,
杨,,
好,,
邪恶;
私有静态映射对立面=新HashMap();
静止的{
对立。放(阴、阳);
相反。把(阳,阴);
对立。把(善,恶);
相反。把(恶,善);
}
公共市场{
返回对立面。得到(这个);
}
}
EnumMap怎么样

public enum Baz {
  yin,
  yang,
  good,
  evil;
  private static final Map<Baz, Baz> opposites = new EnumMap<Baz, Baz>(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);
  }
}
公共枚举Baz{
殷,,
杨,,
好,,
邪恶;
私有静态最终映射对立面=新枚举映射(Baz.class);
静止的{
对立。放(阴、阳);
相反。把(阳,阴);
对立。把(善,恶);
相反。把(恶,善);
}
公共市场{
返回对立面。得到(这个);
}
}
另一种选择:)使用地图。这相当冗长,但这样您可以只定义一次每对,而推断出另一个方向

enum Baz {

    YIN, YANG, GOOD, EVIL;

    private static final Map<Baz, Baz> opposites = new EnumMap<>(Baz.class);

    static {
        opposites.put(YIN, YANG);
        opposites.put(GOOD, EVIL);

        for (Entry<Baz, Baz> entry : opposites.entrySet()) {
            opposites.put(entry.getValue(), entry.getKey());
        }
    }

    public Baz opposite() {
        return opposites.get(this);
    }
}
枚举Baz{ 阴、阳、善、恶; 私有静态最终映射对立面=新枚举映射(Baz.class); 静止的{ 对立。放(阴、阳); 对立。把(善,恶); for(条目:opposities.entrySet()){ put(entry.getValue(),entry.getKey()); } } 公共市集对面(){ 返回对立面。得到(这个); } }
就我个人而言,我最喜欢meriton的第二个例子。

然后是完全的OTT解决方案

public enum Baz {
  yin,
  yang,
  good,
  evil,
  right,
  wrong,
  black,
  white;

  private static class AutoReversingMap<K extends Enum<K>> extends EnumMap<K, K> {
    public AutoReversingMap(Class<K> 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<Baz, Baz> opposites = new AutoReversingMap<Baz>(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);
  }
}
公共枚举Baz{
殷,,
杨,,
好,,
邪恶的,
正确的,
错,,
黑色
白色
私有静态类AutoReversingMap扩展了EnumMap{
公共自动转换映射(类键){
超级(钥匙);
}
//让put同时做正向和反向。
公共K put(K键,K值){
super.put(键、值);
super.put(值、键);
//这里最好返回null,而不是其中一个super的误导性实际返回。
返回null;
}
}
私有静态最终映射对立面=新的自动反转映射(Baz.class);
静止的{
//假设偶数和奇数是相反的。
对于(int i=0;i
几年后,最短、最粗糙的解决方案

public enum Baz {
    YIN, // Use uppercase for enum names!
    YANG,
    GOOD,
    EVIL;

    public Baz opposite() {
        return values()[ordinal() ^ 1];
    }
}
它依赖于这样一个假设,即每个成员都有一个对立面,并且它们是成对排列的。它用一种方法替换字段,希望JVM能够优化整个开销。这在桌面上是合理的,在Android上则不太合理


为了消除开销,我可以在这里使用静态初始值设定项和其他许多解决方案一样。

您也可以使用抽象方法来延迟,这比公认的答案具有类型安全性的好处

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");
            }
        }
    }
}

+1您是对的,您无法使用实际对象使其工作,因为
邪恶
良好
已初始化之前不会定义,但切换它们不起作用,因为在
邪恶
初始化时未定义良好。使用名称的好主意:)用非类型化字符串替换类型化枚举值不是一个好的解决方案。枚举的全部目的是为常量提供类型安全性,而字符串并没有这样做。另外,还有一个更好的解决方案——在静态初始化程序块中执行此操作,正如@meriton在他的回答中指出的。我没有听说过延迟初始化,这似乎是一个好的解决方案,但jcern的字符串版本似乎是最好的方法。对于静态初始化,加上1您刚才复制并粘贴了OldCurmudgeon的答案吗?0.oWell,他使用了HashMap而不是EnumMap,这很重要!:是的,地图上的图案很简单。不,他没有。我们同时发帖。注意,我使用的是
EnumMap
而不是
HashMap
。虽然很吓人,是吗?是的,上面可能是对的。。。这是一个伟大而彻底的答案,但我认为在这种情况下,更简单的答案可能更好。(此外,我可能会添加一个与之相反的值,在这种情况下,这并不理想)我完全同意。这更是为了好玩。我认为@meriton的第二个建议是最整洁的。这不是几年后的事,只是几个月后的事。这是一个很好的答案,我喜欢它的简单。在Java枚举实例上,我更喜欢小写,因为它们实际上与静态最终属性相同。JVM真的会优化出相反的
吗?我不确定JVM优化了多少。@user1494396简单的短方法调用总是内联的,只要JVM认为它与性能相关。调用
values()
通常是正常的,但这是最简单的情况。所以我希望除了字段加载、xor和静态字段加载之外什么都没有。这仍然是一个字段的3倍,但我想找到一个可以测量的真实示例是很困难的。@user1494396如果不是枚举,还应该用大写写什么?编码约定说常量应该是,但它不是
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");
            }
        }
    }
}