比较 Java 枚举成员:== 或 equals()?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1750435/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 22:16:59  来源:igfitidea点击:

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

javaenums

提问by Matt Ball

I know that Java enums are compiled to classes with private constructors and a bunch of public static members. When comparing two members of a given enum, I've always used .equals(), e.g.

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

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

However, I just came across some code that uses the equals operator ==instead of .equals():

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

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

Which operator is the one I should be using?

我应该使用哪个运算符?

采纳答案by Reverend Gonzo

Both are technically correct. If you look at the source code for .equals(), it simply defers to ==.

两者在技术上都是正确的。如果您查看 的源代码.equals(),它只是遵循==

I use ==, however, as that will be null safe.

==但是,我使用,因为这将是空安全的。

回答by Suraj Chandran

In case of enum both are correct and right!!

在枚举的情况下,两者都是正确的!!

回答by Pascal Thivent

Using ==to compare two enum values works, because there is only one object for each enum constant.

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

On a side note, there is actually no need to use ==to write null-safe code, if you write your equals()like this:

附带说明一下,实际上没有必要使用==来编写空安全代码,如果你这样写equals()

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

This is a best practice known as Compare Constants From The Leftthat you definitely should follow.

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

回答by polygenelubricants

Can ==be used on enum?

可以==用在enum吗?

Yes: enums have tight instance controls that allows you to use ==to compare instances. Here's the guarantee provided by the language specification (emphasis by me):

是的:枚举具有严格的实例控制,允许您==用于比较实例。这是语言规范提供的保证(我强调):

JLS 8.9 Enums

An enum type has no instances other than those defined by its enum constants.

It is a compile-time error to attempt to explicitly instantiate an enum type. The final clonemethod in Enumensures that enumconstants can never be cloned, and the special treatment by the serialization mechanism ensures that duplicate instances are never created as a result of deserialization. Reflective instantiation of enum types is prohibited. Together, these four things ensure that no instances of an enumtype exist beyond those defined by the enumconstants.

Because there is only one instance of each enumconstant, it is permissible to use the ==operator in place of the equalsmethod when comparing two object references if it is known that at least one of them refers to an enumconstant. (The equalsmethod in Enumis a finalmethod that merely invokes super.equalson its argument and returns the result, thus performing an identity comparison.)

JLS 8.9 枚举

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

尝试显式实例化枚举类型是编译时错误。final clonein的方法Enum确保enum永远不会克隆常量,并且序列化机制的特殊处理确保永远不会因反序列化而创建重复的实例。禁止枚举类型的反射实例化。总之,这四件事确保了enum除了enum常量定义的那些实例之外,不存在任何类型的实例。

因为只有一个每个实例enum常数,它允许使用的==操作者代替的equals比较两个对象的引用时,如果已知它们中的至少一个是指方法enum常数。(equals方法 inEnum是一种final仅调用super.equals其参数并返回结果的方法,从而执行身份比较。)

This guarantee is strong enough that Josh Bloch recommends, that if you insist on using the singleton pattern, the best way to implement it is to use a single-element enum(see: Effective Java 2nd Edition, Item 3: Enforce the singleton property with a private constructor or an enum type; also Thread safety in Singleton)

这个保证足够强,Josh Bloch 建议,如果你坚持使用单例模式,实现它的最好方法是使用单元素enum(参见:Effective Java 2nd Edition,Item 3:Enforce the singleton property with a私有构造函数或枚举类型;还有单例中的线程安全



What are the differences between ==and equals?

==和之间有什么区别equals

As a reminder, it needs to be said that generally, ==is NOT a viable alternative to equals. When it is, however (such as with enum), there are two important differences to consider:

提醒一下,需要说的是,一般来说,==不是equals. 但是,当它是(例如 with enum)时,需要考虑两个重要差异:

==never throws NullPointerException

==从不扔 NullPointerException

enum Color { BLACK, WHITE };

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

==is subject to type compatibility check at compile time

==在编译时进行类型兼容性检查

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!


Should ==be used when applicable?

应该==在适用时使用吗?

Bloch specifically mentions that immutable classes that have proper control over their instances can guarantee to their clients that ==is usable. enumis specifically mentioned to exemplify.

Bloch 特别提到,对其实例进行适当控制的不可变类可以向其客户端保证==可用。enum特地提到来举例说明。

Item 1: Consider static factory methods instead of constructors

[...] it allows an immutable class to make the guarantee that no two equal instances exist: a.equals(b)if and only if a==b. If a class makes this guarantee, then its clients can use the ==operator instead of the equals(Object)method, which may result in improved performance. Enum types provide this guarantee.

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

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

To summarize, the arguments for using ==on enumare:

总而言之,使用==on的参数enum是:

  • It works.
  • It's faster.
  • It's safer at run-time.
  • It's safer at compile-time.
  • 有用。
  • 它更快。
  • 它在运行时更安全。
  • 在编译时更安全。

回答by ChrisCantrell

Here is a crude timing test to compare the two:

这是一个粗略的时间测试来比较两者:

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

}

Comment out the IFs one at a time. Here are the two compares from above in disassembled byte-code:

一次注释掉一个 IF。以下是上面反汇编字节码中的两个比较:

 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

The first (equals) performs a virtual call and tests the return boolean from the stack. The second (==) compares the object addresses directly from the stack. In the first case there is more activity.

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

I ran this test several times with both IFs one at a time. The "==" is ever so slightly faster.

我用两个 IF 一次一个地运行了这个测试。“==”总是稍微快一点。

回答by Tobias

As others have said, both ==and .equals()work in most cases. The compile time certainty that you're not comparing completely different types of Objects that others have pointed out is valid and beneficial, however the particular kind of bug of comparing objects of two different compile time types would also be found by FindBugs (and probably by Eclipse/IntelliJ compile time inspections), so the Java compiler finding it doesn't add that much extra safety.

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

However:

然而:

  1. The fact that ==never throws NPE in my mind is a disadvantageof ==. There should hardly ever be a need for enumtypes to be null, since any extra state that you may want to express via nullcan just be added to the enumas an additional instance. If it is unexpectedly null, I'd rather have a NPE than ==silently evaluating to false. Therefore I disagree with the it's safer at run-timeopinion; it's better to get into the habit never to let enumvalues be @Nullable.
  2. The argument that ==is fasteris also bogus. In most cases you'll call .equals()on a variable whose compile time type is the enum class, and in those cases the compiler can know that this is the same as ==(because an enum's equals()method can not be overridden) and can optimize the function call away. I'm not sure if the compiler currently does this, but if it doesn't, and turns out to be a performance problem in Java overall, then I'd rather fix the compiler than have 100,000 Java programmers change their programming style to suit a particular compiler version's performance characteristics.
  3. enumsare Objects. For all other Object types the standard comparison is .equals(), not ==. I think it's dangerous to make an exception for enumsbecause you might end up accidentally comparing Objects with ==instead of equals(), especially if you refactor an enuminto a non-enum class. In case of such a refactoring, the It workspoint from above is wrong. To convince yourself that a use of ==is correct, you need to check whether value in question is either an enumor a primitive; if it was a non-enumclass, it'd be wrong but easy to miss because the code would still compile. The only case when a use of .equals()would be wrong is if the values in question were primitives; in that case, the code wouldn't compile so it's much harder to miss. Hence, .equals()is much easier to identify as correct, and is safer against future refactorings.
  1. 这一事实==不会抛出NPE在我心目中是一个不利==。几乎不需要enum类型为null,因为您可能想要表达的任何额外状态都可以作为附加实例null添加到enum。如果出乎意料null,我宁愿有一个 NPE,也不愿==默默地评估为 false。因此,我不同意运行时更安全的观点;最好养成永远不要让enum价值观成为的习惯@Nullable
  2. 该参数==也是伪造的。在大多数情况下,您将调用.equals()一个编译时类型为 enum 类的变量,在这些情况下,编译器可以知道这与==(因为 anenumequals()方法不能被覆盖)相同,并且可以优化函数调用离开。我不确定编译器当前是否这样做,但如果没有这样做,并且结果证明是 Java 整体的性能问题,那么我宁愿修复编译器也不愿让 100,000 个 Java 程序员改变他们的编程风格以适应特定编译器版本的性能特征。
  3. enums是对象。对于所有其他 Object 类型,标准比较是.equals(),不是==。我认为例外是很危险的,enums因为您最终可能会意外地将 Objects 与==而不是进行比较equals(),尤其是当您将 an 重构enum为非枚举类时。在这种重构的情况下,上面的It 工作点是错误的。为了说服自己使用 of==是正确的,您需要检查所讨论的值是 anenum还是原始值;如果它是一个非enum类,它会是错误的但很容易错过,因为代码仍然可以编译。使用的唯一情况.equals()如果所讨论的值是原始值,那将是错误的;在这种情况下,代码将无法编译,因此更难错过。因此,.equals()更容易识别为正确的,并且对未来的重构更安全。

I actually think that the Java language should have defined == on Objects to call .equals() on the left hand value, and introduce a separate operator for object identity, but that's not how Java was defined.

我实际上认为 Java 语言应该在 Objects 上定义 == 以在左侧值上调用 .equals() ,并为对象标识引入一个单独的运算符,但这不是 Java 的定义方式。

In summary, I still think the arguments are in favor of using .equals()for enumtypes.

总之,我仍然认为这些论点支持使用.equals()forenum类型。

回答by Christ

I want to complement polygenelubricants answer:

我想补充 polygenelubricants 答案:

I personally prefer equals(). But it lake the type compatibility check. Which I think is an important limitation.

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

To have type compatibility check at compilation time, declare and use a custom function in your enum.

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

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

With this, you got all the advantage of both solution: NPE protection, easy to read code and type compatibility check at compilation time.

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

I also recommend to add an UNDEFINED value for enum.

我还建议为枚举添加一个 UNDEFINED 值。

回答by Tomas

Using anything other than ==to compare enum constants is nonsense. It's like comparing classobjects with equals– don't do it!

除了==比较枚举常量之外,使用任何其他方法都是无稽之谈。这就像比较class对象equals- 不要这样做!

However, there was a nasty bug (BugId 6277781) in Sun JDK 6u10 and earlier that might be interesting for historical reasons. This bug prevented proper use of ==on deserialized enums, although this is arguably somewhat of a corner case.

但是,由于历史原因,Sun JDK 6u10 及更早版本中存在一个令人讨厌的错误 ( BugId 6277781)。这个错误阻止==了反序列化枚举的正确使用,尽管这可以说是一种极端情况。

回答by ΦXoc? ? Пepeúpa ツ

Enums are classes that return one instance (like singletons) for each enumeration constant declared by public static final field(immutable) so that ==operator could be used to check their equality rather than using equals()method

枚举是为public static final field(不可变)声明的每个枚举常量返回一个实例(如单例)的类,以便==运算符可用于检查它们的相等性而不是使用equals()方法

回答by Pau

I prefer to use ==instead of equals:

我更喜欢使用==而不是equals

Other reason, in addition to the others already discussed here, is you could introduce a bug without realizing it. Suppose you have this enums which is exactly the same but in separated pacakges (it's not common, but it could happen):

除了这里已经讨论的其他原因之外,其他原因是您可能会在没有意识到的情况下引入错误。假设您有这个完全相同的枚举,但在单独的包中(这并不常见,但可能会发生):

First enum:

第一个枚举

package first.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

Second enum:

第二个枚举:

package second.pckg

public enum Category {
    JAZZ,
    ROCK,
    POP,
    POP_ROCK
}

Then suppose you use the equals like next in item.categorywhich is first.pckg.Categorybut you import the second enum (second.pckg.Category) instead the first without realizing it:

然后假设您使用像 next 一样的等号item.categoryfirst.pckg.Category但您导入了第二个枚举 ( second.pckg.Category) 而不是第一个,却没有意识到:

import second.pckg.Category;
...

Category.JAZZ.equals(item.getCategory())

So you will get allways falsedue is a different enum although you expect true because item.getCategory()is JAZZ. And it could be be a bit difficult to see.

所以你会得到 allways falsedue is 一个不同的枚举,尽管你期望 true 因为item.getCategory()is JAZZ。而且可能有点难看。

So, if you instead use the operator ==you will have a compilation error:

因此,如果您改为使用运算符==,则会出现编译错误:

operator == cannot be applied to "second.pckg.Category", "first.pckg.Category"

运算符 == 不能应用于“second.pckg.Category”、“first.pckg.Category”

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory()