Comparing Java enum members: == or 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.
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 inEnum
ensures thatenum
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 anenum
type exist beyond those defined by theenum
constants.Because there is only one instance of each
enum
constant, it is permissible to use the==
operator in place of theequals
method when comparing two object references if it is known that at least one of them refers to anenum
constant. (Theequals
method inEnum
is afinal
method that merely invokessuper.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 ifa==b
. If a class makes this guarantee, then its clients can use the==
operator instead of theequals(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.
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.
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:
- The fact that
==
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
. - 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 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()
for enum
types.
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()