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

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

然而,我刚刚遇到一些使用equals运算符==而不是.equals()的代码:

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

我应该使用哪个操作员?


当前回答

我想补充多基因润滑剂的答案:

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

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

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

这样,您就获得了这两种解决方案的所有优点:NPE保护、易于阅读的代码和编译时的类型兼容性检查。

我还建议为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());
    }   

}

一次评论一个国际单项体育联合会。以下是上面两个比较的分解字节码:

 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

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

我对两个国际单项体育联合会一次进行了几次测试。“==”速度稍快。

可以在枚举上使用==吗?

是:enum具有严格的实例控件,允许您使用==来比较实例。以下是语言规范提供的保证(由我强调):

JLS 8.9枚举枚举类型除了由其枚举常量定义的实例之外,没有其他实例。尝试显式实例化枚举类型是一个编译时错误。Enum中的最后一个clone方法确保永远无法克隆枚举常量,序列化机制的特殊处理确保永远不会由于反序列化而创建重复实例。禁止枚举类型的反射实例化。这四点共同确保了枚举类型的实例不存在于枚举常量定义的实例之外。因为每个枚举常量只有一个实例,所以在比较两个对象引用时,如果已知其中至少一个引用了枚举常量,则允许使用==运算符代替equals方法。(Enum中的equals方法是一个最终方法,它只在其参数上调用super.equals并返回结果,从而执行身份比较。)

乔什·布洛克(Josh Bloch)建议,如果您坚持使用单例模式,最好的实现方法是使用单个元素枚举(请参阅:有效的Java第二版,第3项:使用私有构造函数或枚举类型强制执行单例属性;另请参阅singleton中的线程安全)


==和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上使用==的参数如下:

它起作用了。速度更快。运行时更安全。编译时更安全。

正如其他人所说,==和.equals()在大多数情况下都有效。编译时确定您没有比较其他人指出的完全不同类型的对象是有效和有益的,但是FindBugs(可能还有Eclipse/InIntelliJ编译时检查)也会发现比较两种不同编译时类型的对象的特定类型的错误,因此Java编译器发现它不会增加太多额外的安全性。

然而:

==从未在我的脑海中抛出NPE这一事实是==的缺点。几乎不需要枚举类型为null,因为您可能希望通过null表达的任何额外状态都可以作为附加实例添加到枚举中。如果它意外为空,我宁愿使用NPE而不是==默默地计算为false。所以,我不同意运行时更安全的观点;最好养成一个习惯,永远不要让枚举值为@Nullable。==更快的论点也是假的。在大多数情况下,您将对编译时类型为enum类的变量调用.equals(),在这些情况下,编译器可以知道这与==相同(因为enum的equals()方法不能被重写),并可以优化函数调用。我不确定编译器当前是否做到了这一点,但如果没有做到,并且最终证明是Java整体性能问题,那么我宁愿修复编译器,也不愿让100000名Java程序员更改其编程风格以适应特定编译器版本的性能特征。enums是对象。对于所有其他Object类型,标准比较是.equals(),而不是==。我认为为枚举设置异常是危险的,因为您可能会意外地将Object与==而不是equals()进行比较,尤其是在将枚举重构为非枚举类时。在这种重构的情况下,上面的It work点是错误的。为了让自己相信使用==是正确的,您需要检查所讨论的值是枚举还是基元;如果它是一个非枚举类,那么它是错误的,但很容易错过,因为代码仍然可以编译。唯一错误使用.equals()的情况是,所讨论的值是基元;在这种情况下,代码不会编译,因此很难错过。因此,.equals()更容易识别为正确的,并且更安全地防止将来的重构。

实际上,我认为Java语言应该在左边的值上定义==on Objects to call.equals(),并为对象标识引入一个单独的运算符,但Java并不是这样定义的。

总之,我仍然认为参数支持对枚举类型使用.equals()。

Sonar规则之一是Enum值应与“==”进行比较。原因如下:

使用equals()测试枚举值的相等性是完全有效的,因为枚举是一个Object,每个Java开发人员都知道==不应用于比较Object的内容。同时,在enums上使用==:提供与equals()相同的预期比较(内容)比equals()更安全提供编译时(静态)检查而不是运行时检查出于这些原因,应该优先使用==而不是equals()。

最后但并非最不重要的是,enums上的==可以说比equals()更可读(更不冗长)。

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

另一方面,如果您像这样编写equals(),实际上不需要使用==来编写空安全代码:

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

这是一个被称为“从左侧比较常量”的最佳实践,您绝对应该遵循。