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

发布于 2024-08-11 16:47:28 字数 435 浏览 4 评论 0 原文

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

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

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

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

我应该使用哪一个运算符?

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.

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():

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

Which operator is the one I should be using?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

陌路黄昏 2024-08-18 16:47:28

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

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

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

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

披肩女神 2024-08-18 16:47:28

== 可以用在 enum 上吗?

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

JLS 8.9 枚举

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

尝试显式实例化枚举类型是一个编译时错误。 Enum 中的 final clone 方法保证了 enum 常量永远不会被克隆,序列化机制的特殊处理保证了永远不会出现重复的实例作为反序列化的结果而创建。禁止枚举类型的反射实例化。这四件事共同确保 enum 类型的实例不存在于 enum 常量定义的实例之外。

由于每个enum常量只有一个实例,允许使用==运算符代替equals 方法(如果已知其中至少一个对象引用引用enum 常量)。 (Enum 中的 equals 方法是一个 final 方法,它仅在其参数上调用 super.equals 并返回结果,从而执行身份比较。)


这种保证足够强大,Josh Bloch 建议,如果您坚持使用单例模式,实现它的最佳方法是使用单元素enum (请参阅:Effective Java 第二版,第 3 项:使用私有构造函数或枚举类型强制执行单例属性;还有 Singleton 中的线程安全


==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 上使用 == 的论点是:

  • 它有效。
  • 速度更快了。
  • 运行时更安全。
  • 编译时更安全。

Can == be used on 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 clone method in Enum ensures that enum constants 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 enum type exist beyond those defined by the enum constants.

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

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)


What are the differences between == and 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:

== never throws 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. enum is specifically mentioned to exemplify.

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.

To summarize, the arguments for using == on enum are:

  • It works.
  • It's faster.
  • It's safer at run-time.
  • It's safer at compile-time.
太阳哥哥 2024-08-18 16:47:28

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

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

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

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

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:

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

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

怪我太投入 2024-08-18 16:47:28

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

然而:

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

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

总之,我仍然认为这些论点赞成对 enum 类型使用 .equals()

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.

However:

  1. The fact that == never throws NPE in my mind is a disadvantage of ==. There should hardly ever be a need for enum types to be null, since any extra state that you may want to express via null can just be added to the enum as 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-time opinion; it's better to get into the habit never to let enum values be @Nullable.
  2. The argument that == is faster is 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. enums are Objects. For all other Object types the standard comparison is .equals(), not ==. I think it's dangerous to make an exception for enums because you might end up accidentally comparing Objects with == instead of equals(), especially if you refactor an enum into a non-enum class. In case of such a refactoring, the It works point from above is wrong. To convince yourself that a use of == is correct, you need to check whether value in question is either an enum or a primitive; if it was a non-enum class, 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.

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.

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

后知后觉 2024-08-18 16:47:28

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

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

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
}

然后假设您使用等于 item.category 中的 next,即 first.pckg.Category,但您导入第二个枚举 (second.pckg.Category),而不是首先没有意识到:

import second.pckg.Category;
...

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

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

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

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

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 

I prefer to use == instead of 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.category which is first.pckg.Category but you import the second enum (second.pckg.Category) instead the first without realizing it:

import second.pckg.Category;
...

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

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

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

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

import second.pckg.Category; 
...

Category.JAZZ == item.getCategory() 
遥远的绿洲 2024-08-18 16:47:28

tl;dr

另一种选择是 Objects.equals 实用方法。

Objects.equals( thisEnum , thatEnum )

Objects.equals 用于空安全

等于运算符 == 而不是 .equals()

我应该使用哪一个运算符?

第三个选项是静态 equals 方法在 对象 实用程序类添加到 Java 7 及更高版本。

示例

以下是使用 Month 枚举。

boolean areEqual = Objects.equals( Month.FEBRUARY , Month.JUNE ) ;  // Returns `false`.

优点

我发现这种方法有几个好处:

工作

原理 对象.等于

亲自查看,来自 OpenJDK 的 ="noreferrer">Java 10 源代码:

return 
    ( a == b ) 
    || 
    ( 
        a != null 
        && 
        a.equals( b )
    )
;

tl;dr

Another option is the Objects.equals utility method.

Objects.equals( thisEnum , thatEnum )

Objects.equals for null-safety

equals operator == instead of .equals()

Which operator is the one I should be using?

A third option is the static equals method found on the Objects utility class added to Java 7 and later.

Example

Here’s an example using the Month enum.

boolean areEqual = Objects.equals( Month.FEBRUARY , Month.JUNE ) ;  // Returns `false`.

Benefits

I find a couple benefits to this method:

  • Null-safety
  • Compact, readable

How it works

What is the logic used by Objects.equals?

See for yourself, from the Java 10 source code of OpenJDK:

return 
    ( a == b ) 
    || 
    ( 
        a != null 
        && 
        a.equals( b )
    )
;
笑着哭最痛 2024-08-18 16:47:28

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

使用 equals() 测试枚举值的相等性是完全有效的,因为枚举是一个对象,并且每个 Java 开发人员都知道 == 不应该用于比较对象的内容。同时,在枚举上使用==

  • 提供与 equals() 相同的预期比较(内容)

  • equals() 更空安全

  • 提供编译时(静态)检查而不是运行时检查

出于这些原因,应优先使用 == 而不是 equals()

最后但并非最不重要的一点是,枚举上的 == 可以说比 equals() 更具可读性(更简洁)。

One of the Sonar rules is Enum values should be compared with "==". The reasons are as follows:

Testing equality of an enum value with equals() is perfectly valid because an enum is an Object and every Java developer knows == should not be used to compare the content of an Object. At the same time, using == on enums:

  • provides the same expected comparison (content) as equals()

  • is more null-safe than equals()

  • provides compile-time (static) checking rather than runtime checking

For these reasons, use of == should be preferred to equals().

Last but not least, the == on enums is arguably more readable (less verbose) than equals().

孤独患者 2024-08-18 16:47:28

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

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

}

一次注释掉一个 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

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

我多次使用两个 IF 运行此测试,每次一个。 “==”稍微快一点。

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:

 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.

一抹淡然 2024-08-18 16:47:28

对于枚举来说,两者都是正确的!

In case of enum both are correct and right!!

无尽的现实 2024-08-18 16:47:28

使用 == 以外的任何内容来比较枚举常量都是无意义的。这就像比较class对象与 equals – 不要这样做!

然而,Sun JDK 6u10 及更早版本中存在一个严重的错误 (BugId 6277781)由于历史原因,这可能很有趣。此错误阻止了在反序列化枚举上正确使用 ==,尽管这可以说是一种极端情况。

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

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.

萌吟 2024-08-18 16:47:28

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

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

枕头说它不想醒 2024-08-18 16:47:28

只需添加一件事即可添加到所有其他优秀答案中。当您使用简单的 lambda 时,我更喜欢 equals 而不是 ==,因为您可以使用方法引用。

考虑以下 lambda:

Stream.of(SomeEnum.A, SomeEnum.B).anyMatch(e -> e == SomeEnum.B);
Stream.of(SomeEnum.A, SomeEnum.B).anyMatch(e -> e.equals(SomeEnum.B));

后者可以转换为:

Stream.of(SomeEnum.A, SomeEnum.B).anyMatch(SomeEnum.B::equals));

Just one thing to add to all the other excellent answers. When you use a simple lambda I do prefer equals over ==, because you can use method referencing.

Consider following lambdas:

Stream.of(SomeEnum.A, SomeEnum.B).anyMatch(e -> e == SomeEnum.B);
Stream.of(SomeEnum.A, SomeEnum.B).anyMatch(e -> e.equals(SomeEnum.B));

The later can be converted to:

Stream.of(SomeEnum.A, SomeEnum.B).anyMatch(SomeEnum.B::equals));
小伙你站住 2024-08-18 16:47:28

枚举可以轻松地与 == 一起使用的原因是因为每个定义的实例也是一个单例。所以使用 == 进行身份比较总是有效的。

但是使用 == 因为它与枚举一起使用意味着您的所有代码都与该枚举的使用紧密耦合。

例如:枚举可以实现接口。假设您当前正在使用实现 Interface1 的枚举。如果稍后有人更改它或引入一个新类 Impl1 作为同一接口的实现。然后,如果您开始使用 Impl1 的实例,由于之前使用了 ==,您将需要更改和测试大量代码。

因此,最好遵循被认为是良好做法的做法,除非有任何合理的收益。

The reason enums work easily with == is because each defined instance is also a singleton. So identity comparison using == will always work.

But using == because it works with enums means all your code is tightly coupled with usage of that enum.

For example: Enums can implement an interface. Suppose you are currently using an enum which implements Interface1. If later on, someone changes it or introduces a new class Impl1 as an implementation of same interface. Then, if you start using instances of Impl1, you'll have a lot of code to change and test because of previous usage of ==.

Hence, it's best to follow what is deemed a good practice unless there is any justifiable gain.

滥情空心 2024-08-18 16:47:28

我想补充一下polygenelubricants的答案:

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

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

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

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

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

I want to complement polygenelubricants answer:

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

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.

I also recommend to add an UNDEFINED value for enum.

西瓜 2024-08-18 16:47:28

简而言之,两者各有利弊。

一方面,如其他答案中所述,使用 == 有优势。

另一方面,如果您出于某种原因用不同的方法(普通类实例)替换枚举,那么使用 == 就会让您烦恼。 (BTDT。)

In short, both have pros and cons.

On one hand, it has advantages to use ==, as described in the other answers.

On the other hand, if you for any reason replace the enums with a different approach (normal class instances), having used == bites you. (BTDT.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文