比较 Java 枚举成员:== 还是 equals()?
我知道 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)
{
...
}
...
}
我应该使用哪一个运算符?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
两者在技术上都是正确的。如果您查看
.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.==
可以用在enum
上吗?是的:枚举具有严格的实例控制,允许您使用
==
来比较实例。这是语言规范提供的保证(由我强调):这种保证足够强大,Josh Bloch 建议,如果您坚持使用单例模式,实现它的最佳方法是使用单元素
enum
(请参阅:Effective Java 第二版,第 3 项:使用私有构造函数或枚举类型强制执行单例属性;还有 Singleton 中的线程安全)==
和equals
之间有什么区别?提醒一下,需要指出的是,通常
==
并不是equals
的可行替代方案。然而,当它是时(例如使用enum
),需要考虑两个重要的区别:==
永远不会抛出NullPointerException
= =
在编译时接受类型兼容性检查在适用时是否应该使用
==
?Bloch 特别提到,对其实例具有适当控制的不可变类可以向其客户端保证
==
可用。特别提到enum
来举例说明。总而言之,在
enum
上使用==
的论点是:Can
==
be used onenum
?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):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
==
andequals
?As a reminder, it needs to be said that generally,
==
is NOT a viable alternative toequals
. When it is, however (such as withenum
), there are two important differences to consider:==
never throwsNullPointerException
==
is subject to type compatibility check at compile timeShould
==
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.To summarize, the arguments for using
==
onenum
are:使用
==
比较两个枚举值是可行的,因为每个枚举常量只有一个对象。顺便说一句,如果您像这样编写
equals()
,实际上不需要使用==
来编写空安全代码:这是最佳实践称为从左侧比较常量,您绝对应该遵循。
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 yourequals()
like this:This is a best practice known as Compare Constants From The Left that you definitely should follow.
正如其他人所说,
==
和.equals()
在大多数情况下都有效。您没有比较其他人指出的完全不同类型的对象的编译时确定性是有效且有益的,但是 FindBugs 也会发现比较两种不同编译时类型的对象的特定类型的错误(并且可能由Eclipse/IntelliJ 编译时检查),因此 Java 编译器发现它并不会增加太多额外的安全性。然而:
==
在我的脑海里从来不会抛出 NPE,这是==
的一个缺点。几乎不需要将enum
类型设置为null
,因为您可能想要通过null
表达的任何额外状态都可以是作为附加实例添加到enum
中。如果它意外地为null
,我宁愿有一个 NPE,也不愿==
默默地评估为 false。因此我不同意运行时更安全的观点;最好养成永远不要让enum
值为@Nullable
的习惯。==
更快的论点也是虚假的。在大多数情况下,您将在编译时类型为枚举类的变量上调用.equals()
,在这些情况下,编译器可以知道这与==< /code> (因为
enum
的equals()
方法无法被重写)并且可以优化函数调用。我不确定编译器目前是否这样做,但如果没有,并且结果证明这是 Java 整体的性能问题,那么我宁愿修复编译器,也不愿让 100,000 名 Java 程序员改变他们的编程风格以适应特定编译器版本的性能特征。枚举
是对象。对于所有其他对象类型,标准比较是.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:
==
never throws NPE in my mind is a disadvantage of==
. There should hardly ever be a need forenum
types to benull
, since any extra state that you may want to express vianull
can just be added to theenum
as an additional instance. If it is unexpectedlynull
, 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 letenum
values be@Nullable
.==
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 anenum
'sequals()
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.enums
are Objects. For all other Object types the standard comparison is.equals()
, not==
. I think it's dangerous to make an exception forenums
because you might end up accidentally comparing Objects with==
instead ofequals()
, especially if you refactor anenum
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 anenum
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()
forenum
types.我更喜欢使用
==
而不是equals
:除了这里已经讨论的其他原因之外,其他原因是您可能会在没有意识到的情况下引入错误。假设您有一个完全相同的枚举,但位于单独的包中(这并不常见,但可能会发生):
First enum:
Second enum:
然后假设您使用等于
item.category
中的 next,即first.pckg.Category
,但您导入第二个枚举 (second.pckg.Category
),而不是首先没有意识到:所以你总是会得到
false
由于是一个不同的枚举,尽管你期望 true 因为item.getCategory()
是JAZZ
。而且可能有点难以看到。因此,如果您使用运算符
==
,则会出现编译错误:I prefer to use
==
instead ofequals
: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:
Second enum:
Then suppose you use the equals like next in
item.category
which isfirst.pckg.Category
but you import the second enum (second.pckg.Category
) instead the first without realizing it:So you will get allways
false
due is a different enum although you expect true becauseitem.getCategory()
isJAZZ
. And it could be be a bit difficult to see.So, if you instead use the operator
==
you will have a compilation error:tl;dr
另一种选择是
Objects.equals
实用方法。Objects.equals
用于空安全第三个选项是静态
equals
方法在对象
实用程序类添加到 Java 7 及更高版本。示例
以下是使用
Month
枚举。优点
我发现这种方法有几个好处:
true
false
NullPointerException
工作
原理
对象.等于
?亲自查看,来自 OpenJDK 的 ="noreferrer">Java 10 源代码:
tl;dr
Another option is the
Objects.equals
utility method.Objects.equals
for null-safetyA third option is the static
equals
method found on theObjects
utility class added to Java 7 and later.Example
Here’s an example using the
Month
enum.Benefits
I find a couple benefits to this method:
true
false
NullPointerException
How it works
What is the logic used by
Objects.equals
?See for yourself, from the Java 10 source code of OpenJDK:
Sonar 规则之一是
枚举值应与“==”进行比较
。原因如下:最后但并非最不重要的一点是,枚举上的
==
可以说比equals()
更具可读性(更简洁)。One of the Sonar rules is
Enum values should be compared with "=="
. The reasons are as follows:Last but not least, the
==
on enums is arguably more readable (less verbose) thanequals()
.这是一个比较两者的粗略时序测试:
一次注释掉一个 IF。以下是上面反汇编字节码中的两个比较:
第一个(等于)执行虚拟调用并测试堆栈中的返回布尔值。第二个 (==) 直接比较堆栈中的对象地址。在第一种情况下,活动较多。
我多次使用两个 IF 运行此测试,每次一个。 “==”稍微快一点。
Here is a crude timing test to compare the two:
Comment out the IFs one at a time. Here are the two compares from above in disassembled byte-code:
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.
对于枚举来说,两者都是正确的!
In case of enum both are correct and right!!
使用
==
以外的任何内容来比较枚举常量都是无意义的。这就像比较class
对象与equals
– 不要这样做!然而,Sun JDK 6u10 及更早版本中存在一个严重的错误 (BugId 6277781)由于历史原因,这可能很有趣。此错误阻止了在反序列化枚举上正确使用
==
,尽管这可以说是一种极端情况。Using anything other than
==
to compare enum constants is nonsense. It's like comparingclass
objects withequals
– 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.枚举是一种类,它为
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 usingequals()
method只需添加一件事即可添加到所有其他优秀答案中。当您使用简单的 lambda 时,我更喜欢
equals
而不是==
,因为您可以使用方法引用。考虑以下 lambda:
后者可以转换为:
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:
The later can be converted to:
枚举可以轻松地与 == 一起使用的原因是因为每个定义的实例也是一个单例。所以使用 == 进行身份比较总是有效的。
但是使用 == 因为它与枚举一起使用意味着您的所有代码都与该枚举的使用紧密耦合。
例如:枚举可以实现接口。假设您当前正在使用实现 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.
我想补充一下polygenelubricants的答案:
我个人更喜欢equals()。但它忽略了类型兼容性检查。我认为这是一个重要的限制。
要在编译时进行类型兼容性检查,请在枚举中声明并使用自定义函数。
这样,您就可以获得两种解决方案的所有优点: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.
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.
简而言之,两者各有利弊。
一方面,如其他答案中所述,使用
==
有优势。另一方面,如果您出于某种原因用不同的方法(普通类实例)替换枚举,那么使用
==
就会让您烦恼。 (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.)