java - 比较Java枚举成员, ==还是equals( ) ?

  显示原文与译文双语对照的内容

我知道Java枚举被编译为具有私有构造函数和一组公共静态成员的类。 比较给定枚举的两个成员时,我总是使用 .equals(),e.g.


public useEnums(SomeEnum a)
{
 if(a.equals(SomeEnum.SOME_ENUM_VALUE))
 {
. . .
 }
. . .
}

但是,我刚刚碰到了使用equals运算符 ==的代码:


public useEnums2(SomeEnum a)
{
 if(a == SomeEnum.SOME_ENUM_VALUE)
 {
. . .
 }
. . .
}

在哪一个是correct,我一直在编程在Java中对于 5 + 年上,而我觉得自己理解两者之间的差异- 但我还在纠结我的头前 我应该使用哪个运算符?

时间:

== 是否可以在 enum 上使用?

是:枚举具有紧密的实例控件,允许你使用 == 来比较实例。 以下是语言规范提供的保证:

JLS 8.9枚举

枚举类型除了由枚举常量定义的其他实例之外没有其他实例。

试图显式实例化枚举类型是compile-time错误。 在 enumfinal clone 法永远不能克隆,并且可以确保 enum 常数的特殊处理由序列化机制保证了永远不会创建实例副本由于采用反序列化。 禁止枚举类型的反射实例化。 总之,这四种情况确保了 enum 类型的实例不存在于 enum 常量定义的范围之外。

因为只有每个节点的一个实例 enum 常数,允许对校准使用 == 运算符来代替 equals的方法比较两个对象引用时如果众所周知,它们中至少有一个是指一个 enum 。 ( 在 enumequals 法是一个,这仅仅调用 super.equalsfinal 方法在它的参数并返回结果,因此执行一个标识对比) 。

这个保证非常强大,Josh Bloch推荐,如果你坚持使用singleton模式,实现它的最佳方法是使用 single-element enum ( 参见: Effective Java 2版本,项目 3: 在单例 ),强制使用单例还带有私有构造函数的属性或者一个枚举类型 ;线程安全性


==equals 之间的区别是什么?

作为一个提醒,需要说的是,== 不是 equals的一个可行的替代方案。 当它是( 比如 enum ) 时,需要考虑两个重要的差异:

== 从不引发 NullPointerException


enum Color { BLACK, WHITE };

Color nothing = null;
if (nothing == Color.BLACK);//runs fine
if (nothing.equals(Color.BLACK));//throws NullPointerException

== 在编译时服从类型兼容性检查


enum Color { BLACK, WHITE };
enum Chiral { LEFT, RIGHT };

if (Color.BLACK.equals(Chiral.LEFT));//compiles fine
if (Color.BLACK == Chiral.LEFT);//DOESN'T COMPILE!!! Incompatible types!


是否应在适用时使用 ==

Bloch特别提到,对它的实例有适当控制的不可变类可以保证他们的客户可以使用 ==enum 被特别提及以举例说明。

项目 1: 考虑静态工厂方法而不是构造函数

[...] 允许不存在的类保证不存在两个相等的实例: a.equals(b) 如果且仅当 a==b 。 如果一个类做出了这个保证,那么它的客户端可以使用 == 运算符而不是 equals(Object) 方法,这可能会导致性能提高。 枚举类型提供这里保证。

enum are,来总结一下,这个参数用于使用

  • 它可以工作。
  • 快一点。
  • 在run-time上更安全。
  • 在compile-time上更安全。

使用 == 比较两个枚举值是有效的,因为每个枚举常量只有一个对象。

有一点要注意,这里根本没有需要使用 == 要写null安全的代码自己编写 equals() 所示:


public useEnums(SomeEnum a)
{
 if(SomeEnum.SOME_ENUM_VALUE.equals(a))
 {
. . .
 }
. . .
}

这是一个最佳实践,称为比较常量来自左,你绝对应该遵循。

就像其他人所说的,大多数情况下 ==.equals() 都在工作。 编译时,你不比较完全不同类型的对象,其他人指出的确定是有效和有益的但是 Bug的比较对象的两个不同的编译时类型特定的还会发现由 FindBugs ( 可能由 Eclipse/intellij 编译时间检查),所以找到它的Java编译器不会添加那多的额外的安全。

然而:

  1. 事实上,== 从不抛出NPE我心目中是一个缺点的==enum 类型几乎永远都不需要 null,因为你可能希望通过 null 表示的任何额外状态都可以作为附加实例添加到 enum 。 如果是意外比 ==null,我愿意有一个NPE默默地计算为 false 。 因此我不同意这个则更保险在 run-time 看来;它是更好的来养成这个习惯永远不要让被 @Nullableenum 值。
  2. == 速度更快的参数也是假的。 在大多数情况下,你将调用 .equals()的编译时间类型为枚举类的变量,在这种情况下编译器可以知道这与 == ( 因为方法的enumequals() 不能被重写) 相同,并且可以优化函数调用。 我不确定编译器当前是否这么做,但如果它不存在,结果是Java整体的性能问题,那么我宁愿修复编译器的编程风格,以适应特定编译器性能的编译器性能。
  3. enums 是对象。对于所有其他对象类型,标准比较是 .equals(),而不是 == 。 我认为这是很危险的例外地提到 enums 因为你最后可能会意外地比较具有 == 对象而不是 equals(),特别是如果你一个 enum 重构为一个non-enum类。 以大于都是在 wrong,以防这样的重构时,它单独存在 要知道 == 是正确的,你需要知道有问题的值是一个 enum 或者一个原语;如果它是一个非 enum 类,这很容易丢失,因为代码仍然在编译。 在 .equals()的情况下,错误的唯一情况是如果有问题的值是基元;在这种情况下,代码甚至不会编译,因此会更难忽略。 因此,.equals() 更容易识别为正确的,并且对于将来的重构更安全。

我实际上认为Java语言应该在左边的值上定义==来调用. equals(),并为对象标识引入一个单独的操作符,但这不是Java定义的。

总之,我仍然认为参数有利于使用 .equals() 作为 enum 类型。

下面是用于比较这两者的粗略计时测试:


import java.util.Date;

public class EnumCompareSpeedTest {

 static enum TestEnum {ONE, TWO, THREE }

 public static void main(String [] args) {

 Date before = new Date();
 int c = 0;

 for(int y=0;y<5;++y) {
 for(int x=0;x<Integer.MAX_VALUE;++x) {
 if(TestEnum.ONE.equals(TestEnum.TWO)) {++c;}
 if(TestEnum.ONE == TestEnum.TWO){++c;} 
 }
 }

 System.out.println(new Date().getTime() - before.getTime());
 } 

}

每次注释出一个参数。 下面是反汇编byte-code中的两个比较:


 21 getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 24 getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 27 invokevirtual EnumCompareSpeedTest$TestEnum.equals(java.lang.Object) : boolean [28]
 30 ifeq 36

 36 getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19]
 39 getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25]
 42 if_acmpne 48

第一个( 等于) 执行一个虚拟调用并测试堆栈中的返回布尔值。 第二个( == ) 直接从堆栈中比较对象地址。 在第一种情况下,有更多的活动。

我在这个测试中运行了几次,每次都是一个。 "=="的速度是如此的快。

我想补充polygenelubricants回答:

我个人喜欢 equals(),但它在类型兼容性检查中。 我认为这是一个重要的限制。

要在编译时检查类型兼容性,请在枚举中声明并使用自定义函数。


public boolean isEquals(enumVariable)//compare constant from left
public static boolean areEqual(enumVariable, enumVariable2)//compare two variable

使用这里方法,你可以获得两种解决方案的全部优势: 编译时的NPE保护,易于读取代码和类型兼容性检查。

我还建议为枚举添加一个未定义的值。

...