【问题标题】:Java enum- Cannot reference a field before it is definedJava枚举-在定义之前无法引用字段
【发布时间】:2012-09-25 22:58:01
【问题描述】:

我有一个像下面这样的枚举,但是 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相反 对象。有没有可能的解决方法?在此示例中,yingood 之前是否定义了 yangbad 的空占位符?

【问题讨论】:

    标签: java enums


    【解决方案1】:

    用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。

    【讨论】:

    • 我没有听说过延迟初始化,这似乎是一个很好的解决方案,但 jcern 的 String 版本似乎是最好的方法。
    • 加1用于静态初始化
    【解决方案2】:

    你可以试试这样的:

    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 的递归引用一起工作。

    【讨论】:

    • +1 你是对的,你不能让它使用实际的对象来工作,因为 evil 直到 good 已经初始化之后才会被定义,但是切换它们不会'无法正常工作,因为在初始化 evil 时未定义 good。使用名称的好主意:)
    • 用非类型化字符串替换类型化枚举值不是一个好的解决方案。枚举的全部意义在于为常量提供类型安全,而字符串不会给你。此外,还有一个更好的解决方案 - 正如@meriton 在他的回答中指出的那样,在静态初始化程序块中执行此操作。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 这个答案是错误的:匿名类的混淆不会延迟读取静态字段,具体来说,Baz.yin.oppositeHolder.oppositenull。所有这些都会使编译器感到困惑,以至于它不再警告您在分配静态最终字段之前读取它。
      • 非常正确,我打算编辑这个但忘记了。现已更新为工作版本。
      • ...您如何确保在有人阅读相反的内容之前调用 setOpposite?
      • 已更新。有很多方法可以达到这种效果。在构造函数中调用实例方法是不受欢迎的,但为了简洁起见,我离开了这种方式。
      • ... 弄错了:静态字段在分配之前被读取,特别是Baz.yin.getOpposite() 返回null ...
      【解决方案5】:

      另一种选择:) 使用地图。这很冗长,但这样你可以每对只定义一次,然后推断另一个方向。

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

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

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

        • 您是否只是复制并粘贴了 OldCurmudgeon 的答案? 0.o
        • 好吧,他使用了 HashMap 而不是 EnumMap,这很重要! :D 但是,地图模式非常简单。
        • 不,他没有。我们同时发布。请注意,我使用EnumMap 而不是HashMap。很诡异吧?
        【解决方案7】:

        然后是完全 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);
          }
        }
        

        【讨论】:

        • 是的,夸张的可能是对的......这是一个很好而彻底的答案,但我认为在这种情况下更简单的答案可能会更好。 (另外,我可能会添加一个与其相反的值,在这种情况下,这并不理想)
        • 我完全同意。这更多的是为了一点乐趣。我认为@meriton 的第二个建议是最简洁的。
        【解决方案8】:

        多年后,最短和最 hacky 的解决方案

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

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

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

        【讨论】:

        • 不是几年之后,只有几个月。这是一个很好的答案,我喜欢它的简单性。我更喜欢 Java 枚举实例上的小写字母,因为它们实际上与静态最终属性相同。 JVM 真的会优化出opposite 吗?我不确定 JVM 优化了多少。
        • @user1494396 只要 JVM 认为它与性能相关,简单的短方法调用就会总是内联。对values() 的调用通常是includes cloning,但这是escape analysis 最简单的情况。所以我希望只剩下场负载、异或和静态场负载。这仍然是一个领域的 3 倍,但我想找到一个可以测量的真实世界的例子是很困难的。
        • @user1494396 如果不是枚举,还应该用大写写什么?编码约定说常量应该是,但它没有解释常量是什么。也许是一个compile-time constant,也许是一个指向不可变对象的最终(静态?)变量?
        • 老实说,静态变量和枚举值应该总是大写。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-18
        • 2016-07-15
        • 1970-01-01
        相关资源
        最近更新 更多