【问题标题】:Is it OK to use == on enums in Java?可以在 Java 中的枚举上使用 == 吗?
【发布时间】:2010-10-06 17:50:10
【问题描述】:

可以在 Java 中的枚举上使用 ==,还是需要使用 .equals()?在我的测试中,== 总是有效,但我不确定我是否能保证这一点。特别是,枚举上没有 .clone() 方法,所以我不知道是否有可能获得一个枚举,.equals() 将返回与 == 不同的值。

例如,这样可以吗:

public int round(RoundingMode roundingMode) {
  if(roundingMode == RoundingMode.HALF_UP) {
    //do something
  } else if (roundingMode == RoundingMode.HALF_EVEN) {
    //do something
  }
  //etc
}

还是需要这样写:

public int round(RoundingMode roundingMode) {
  if(roundingMode.equals(RoundingMode.HALF_UP)) {
    //do something
  } else if (roundingMode.equals(RoundingMode.HALF_EVEN)) {
    //do something
  }
  //etc
}

【问题讨论】:

  • @assylias 这个问题排在第一位。也许标记 ♦ 注意,因为我不确定这两者是否应该合并。
  • @MattBall 我认为引用 JLS 的问题的答案是最好的答案,这就是我选择关闭这个问题的原因。

标签: java syntax enums


【解决方案1】:

只要我的 2 美分:这里是 Enum.java 的代码,由 Sun 发布,也是 JDK 的一部分:

public abstract class Enum<E extends Enum<E>>
    implements Comparable<E>, Serializable {

    // [...]

    /**
     * Returns true if the specified object is equal to this
     * enum constant.
     *
     * @param other the object to be compared for equality with this object.
     * @return  true if the specified object is equal to this
     *          enum constant.
     */
    public final boolean equals(Object other) { 
        return this==other;
    }


}

【讨论】:

  • 谢谢!我想如果我只是想用编译器进入 .equals() 我会看到这个......
【解决方案2】:

是的,== 很好 - 保证每个值只有一个引用。

但是,有一种更好的方式来编写你的 round 方法:

public int round(RoundingMode roundingMode) {
  switch (roundingMode) {
    case HALF_UP:
       //do something
       break;
    case HALF_EVEN:
       //do something
       break;
    // etc
  }
}

一个甚至更好的方法是将功能放在枚举本身中,所以你可以调用roundingMode.round(someValue)。这触及了 Java 枚举的核心——它们是面向对象的枚举,不像其他地方的“命名值”。

编辑:规范不是很清楚,但section 8.9 声明:

枚举类型的主体可能包含 枚举常量。枚举常量 定义枚举类型的实例。 枚举类型没有其他实例 比那些由它的枚举定义的 常量。

【讨论】:

  • 我很愿意相信你的话,但如果你能提供一些官方文档的链接会更好......
  • 当不同情况之间有很多重叠时,switch 没有用。另外,RoundingMode 是 java.math 的一部分,所以我不能向它添加方法。
  • 哦——你怀疑 Jon Skeet?你来这里的时间不长;)
  • switch 语句中的枚举?不知道这是可能的。总有一天我得试一试。
  • 使用抽象方法在枚举中封装逻辑是枚举的真正力量。它使您的代码更加健壮;当你以后添加一个新的枚举值时,编译器会强制你实现相关的逻辑,你不用记得给多个switch语句添加一个case。
【解决方案3】:

是的,就好像您为枚举中的每个值创建了单例实例:

公共抽象类 RoundingMode { 公共静态最终 RoundingMode HALF_UP = new RoundingMode(); 公共静态最终 RoundingMode HALF_EVEN = new RoundingMode(); 私人舍入模式(){ // 私有作用域阻止该类之外的任何子类型 } }

然而enum 构造为您带来了各种好处:

  • 每个实例的 toString() 打印代码中给出的名称。
  • (如另一篇文章中所述,)枚举类型的变量可以使用switch-case 控制结构与常量进行比较。
  • 枚举中的所有值都可以使用为每个枚举类型“生成”的values 字段进行查询
  • 这是一个关于身份比较的重要内容:枚举值可以在序列化过程中存活而无需克隆。

序列化是一个大问题。如果我使用上面的代码而不是枚举,那么身份平等的行为方式如下:

RoundingMode original = RoundingMode.HALF_UP; 断言(RoundingMode.HALF_UP == 原始); // 通过 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(原始); oos.flush(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); RoundingMode 反序列化 = (RoundingMode) ois.readObject(); 断言(RoundingMode.HALF_UP == 反序列化); // 失败 断言(RoundingMode.HALF_EVEN == 反序列化); // 失败

可以在不使用枚举的情况下解决此问题,使用涉及writeReplacereadResolve 的技术,(请参阅http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html)...

我想重点是——Java 不遗余力地允许您使用枚举值的身份来测试相等性;这是一种鼓励的做法。

【讨论】:

  • @DavidI。感谢更新。这是一个非常令人不安的错误,很高兴知道!
  • @DilumRanatunga 一开始我以为这会影响到我,但在通过 RMI 连接传递它们后它们似乎工作正常。
【解决方案4】:

== 比较两个对象的引用。对于枚举,保证只有一个实例,因此对于任何两个相同的枚举,== 都会为真。

参考:

http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-tricks

(在 Sun 文档中找不到任何内容)

【讨论】:

    【解决方案5】:

    这里有一些你可能会觉得有趣的邪恶代码。 :D

    public enum YesNo {YES, NO}
    
    public static void main(String... args) throws Exception {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        YesNo yesNo = (YesNo) unsafe.allocateInstance(YesNo.class);
    
        Field name = Enum.class.getDeclaredField("name");
        name.setAccessible(true);
        name.set(yesNo, "YES");
    
        Field ordinal = Enum.class.getDeclaredField("ordinal");
        ordinal.setAccessible(true);
        ordinal.set(yesNo, 0);
    
        System.out.println("yesNo " + yesNo);
        System.out.println("YesNo.YES.name().equals(yesNo.name()) "+YesNo.YES.name().equals(yesNo.name()));
        System.out.println("YesNo.YES.ordinal() == yesNo.ordinal() "+(YesNo.YES.ordinal() == yesNo.ordinal()));
        System.out.println("YesNo.YES.equals(yesNo) "+YesNo.YES.equals(yesNo));
        System.out.println("YesNo.YES == yesNo " + (YesNo.YES == yesNo));
    }
    

    【讨论】:

    • @Peter 你能把这段代码的导入包括进来吗?找不到Unsafe.class
    • @rumman0786 jaxenter.com/… 看看这个链接
    【解决方案6】:

    枚举是阻塞多态代码的好地方。

    enum Rounding {
      ROUND_UP {
        public int round(double n) { ...; }
      },
      ROUND_DOWN {
        public int round(double n) { ...; }
      };
    
      public abstract int round(double n);
    }
    
    int foo(Rounding roundMethod) {
      return roundMethod.round(someCalculation());
    }
    
    int bar() {
      return foo(Rounding.ROUND_UP);
    }
    

    【讨论】:

    • 是的,但我没有 java.math.RoundingMode,所以我不能这样做。
    【解决方案7】:

    请注意,通过 RMI/IIOP 传输枚举时会出现问题。看到这个帖子:

    http://www.velocityreviews.com/forums/t390342-enum-equality.html

    【讨论】:

    【解决方案8】:

    == 通常没问题,== 和.equals() 都有优点。我个人更喜欢在比较对象时总是使用.equals(),包括enums。另请参阅此讨论:

    Comparing Java enum members: == or equals()?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-21
      • 1970-01-01
      • 2015-03-02
      • 1970-01-01
      相关资源
      最近更新 更多