在Java枚举中使用==可以吗?


111

是否可以==在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的问题的答案是最好的答案,这就是为什么我选择关闭此答案。
assylias 2012年

Answers:


149

仅需2美分:这是Sun发布的Enum.java的代码,并且是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;
    }


}

4
谢谢!我想如果我只是想进入编译器的.equals()程序,我会看到这个...
Kip

77

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

但是,有一种更好的编写舍入方法的方法:

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

甚至更好的做这件事的方式是把枚举本身的功能,所以你可以只调用roundingMode.round(someValue)。这是Java枚举的核心-它们是面向对象的枚举,与其他地方的“命名值”不同。

编辑:规范不是很清楚,但第8.9节指出:

枚举类型的主体可以包含枚举常量。枚举常量定义枚举类型的实例。枚举类型除了由其枚举常量定义的实例外,没有其他实例。


我很乐意为您效忠,但是如果您可以提供一些更好的官方文档的链接,那么……
Kip

当不同情况之间有很多重叠时,切换功能就没有用。另外,RoundingMode是java.math的一部分,因此我无法向其中添加方法。
Kip

2
哦-您怀疑乔恩·斯基特(Jon Skeet)吗?您已经在这里

枚举switch语句?不知道那是可能的。我将有一天要尝试一下。
luiscubal

使用抽象方法将逻辑封装在枚举中是枚举的真正力量。它使您的代码更加健壮。以后添加新的枚举值时,编译器将强制您实现相关的逻辑,而您不必记住要为多个switch语句添加大小写。
安德鲁·斯旺

13

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

公共抽象类RoundingMode {
  公共静态最终RoundingMode HALF_UP = new RoundingMode();
  公共静态最终RoundingMode HALF_EVEN = new RoundingMode();

  私人RoundingMode(){
    //私有作用域可防止此类之外的任何子类型
  }
}

但是,该enum构造为您带来各种好处:

  • 每个实例的toString()打印代码中给定的名称。
  • (如另一篇文章所述),可以使用switch-case控制结构将枚举类型的变量与常量进行比较。
  • 可以使用values为每种枚举类型“生成” 的字段查询枚举中的所有值
  • 这是最大的身份比较:枚举值可以在不克隆的情况下进行序列化。

序列化是一个很大的难题。如果我使用上面的代码而不是枚举,则身份相等的行为如下:

RoundingMode原始= RoundingMode.HALF_UP;
断言(RoundingMode.HALF_UP ==原始); //通过

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos =新的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 ==反序列化); //失败

可以使用涉及writeReplace和的技术解决此问题而无需枚举readResolve(请参阅http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html)...

我想关键是-Java竭尽全力允许您使用枚举值的身份来测试相等性;这是一种鼓励的做法。



@DavidI。感谢更新。这是一个非常令人不安的错误,很高兴知道!
Dilum Ranatunga

1
@DilumRanatunga我认为起初这会影响我,但是在通过RMI连接传递它们之后,它们似乎工作正常。
David I.


6

这是一些您可能会发现有趣的邪恶代码。: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));
}

1
@Peter您能否包括此代码的导入?找不到Unsafe.class
rumman0786 '18

3

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

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

1
是的,但是我不拥有java.math.RoundingMode,因此我无法执行此操作。
Kip


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.