Is it OK to use ==
on enums in Java, or do I need to use .equals()
? In my testing, ==
always works, but I'm not sure if I'm guaranteed of that. In particular, there is no .clone()
method on an enum, so I don't know if it is possible to get an enum for which .equals()
would return a different value than ==
.
For example, is this OK:
public int round(RoundingMode roundingMode) {
if(roundingMode == RoundingMode.HALF_UP) {
//do something
} else if (roundingMode == RoundingMode.HALF_EVEN) {
//do something
}
//etc
}
Or do I need to write it this way:
public int round(RoundingMode roundingMode) {
if(roundingMode.equals(RoundingMode.HALF_UP)) {
//do something
} else if (roundingMode.equals(RoundingMode.HALF_EVEN)) {
//do something
}
//etc
}
-
Yes, == is fine - there's guaranteed to be just a single reference for each value.
However, there's a better way of writing your round method:
public int round(RoundingMode roundingMode) { switch (roundingMode) { case HALF_UP: //do something break; case HALF_EVEN: //do something break; // etc } }
An even better way of doing it is to put the functionality within the enum itself, so you could just call
roundingMode.round(someValue)
. This gets to the heart of Java enums - they're object-oriented enums, unlike the "named values" found elsewhere.EDIT: The spec isn't very clear, but section 8.9 states:
The body of an enum type may contain enum constants. An enum constant defines an instance of the enum type. An enum type has no instances other than those defined by its enum constants.
Kip : I'd love to take your word for it, but if you could provide a link to some official documentation that'd be better...Jon Skeet : Will look for the relevant bit of the spec or Java docs.Joel Coehoorn : Is it okay? That's kind of the whole point!Kip : switch isn't useful when there is a lot of overlap between different cases. Also, RoundingMode is part of java.math, so I can't add a method to it.Joel Coehoorn : Oh-- and you doubt Jon Skeet? You haven't been around here very long ;)Andrea Francia : +1 for note about the "better way".luiscubal : enums in switch statements? Didn't know that was possible. I'll have to try it out one day.Andrew Swan : Encapsulating logic within enums using abstract methods is the real power of enums. It makes your code much more robust; when you add a new enum value in the future, the compiler will force you to implement the relevant logic, you don't have to remember to add a case to multiple switch statements.Rob : Better than the accepted answer. -
== compares the references of two objects. For enums, it is guaranteed that there will only be one instance, and therefore for any two enums that are the same, == will be true.
Reference:
http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-tricks
(couldn't find anything in the Sun docs)
-
Just my 2 cents: Here is the code for Enum.java, as published by Sun, and part of the JDK:
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { // [...] /** * Returns true if the specified object is equal to this * enum constant. * * @param other the object to be compared for equality with this object. * @return true if the specified object is equal to this * enum constant. */ public final boolean equals(Object other) { return this==other; } }
Kip : Thanks! I guess if I had just thought to step into .equals() with the compiler I would have seen this... -
Here is some evil code you might find interesting. :D
public enum YesNo {YES, NO} public static void main(String... args) throws Exception { Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); Unsafe unsafe = (Unsafe) field.get(null); YesNo yesNo = (YesNo) unsafe.allocateInstance(YesNo.class); Field name = Enum.class.getDeclaredField("name"); name.setAccessible(true); name.set(yesNo, "YES"); Field ordinal = Enum.class.getDeclaredField("ordinal"); ordinal.setAccessible(true); ordinal.set(yesNo, 0); System.out.println("yesNo " + yesNo); System.out.println("YesNo.YES.name().equals(yesNo.name()) "+YesNo.YES.name().equals(yesNo.name())); System.out.println("YesNo.YES.ordinal() == yesNo.ordinal() "+(YesNo.YES.ordinal() == yesNo.ordinal())); System.out.println("YesNo.YES.equals(yesNo) "+YesNo.YES.equals(yesNo)); System.out.println("YesNo.YES == yesNo " + (YesNo.YES == yesNo)); }
Chii : dont be evil! btw, you can do a similar thing with strings that are interned... -
Yes, it is as if you had created singleton instances for each value in the enum:
public abstract class RoundingMode { public static final RoundingMode HALF_UP = new RoundingMode(); public static final RoundingMode HALF_EVEN = new RoundingMode(); private RoundingMode() { // private scope prevents any subtypes outside of this class } }
However, the
enum
construct gives you various benefits:- Each instance's toString() prints the name given in code.
- (As mentioned in another post,) a variable of the enum type can be compared against constants using the
switch-case
control structure. - All the values in the enumeration can be queried using the
values
field that is 'generated' for each enum type - Here's the big one w.r.t identity comparisons: enum values survive serialization without cloning.
The serialization is a big gotchya. If I were to use the code above instead of an enum, here's how identity equality would behave:
RoundingMode original = RoundingMode.HALF_UP; assert (RoundingMode.HALF_UP == original); // passes ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(original); oos.flush(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); RoundingMode deserialized = (RoundingMode) ois.readObject(); assert (RoundingMode.HALF_UP == deserialized); // fails assert (RoundingMode.HALF_EVEN == deserialized); // fails
You can address this issue without enum, using a technique that involves
writeReplace
andreadResolve
, (see http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html)...I guess the point is -- Java goes out of its way to allow you use enum values' identities for testing equality; it is an encouraged practice.
-
Enums are a great place to jam polymorphic code.
enum Rounding { ROUND_UP { public int round(double n) { ...; } }, ROUND_DOWN { public int round(double n) { ...; } }; public abstract int round(double n); } int foo(Rounding roundMethod) { return roundMethod.round(someCalculation()); } int bar() { return foo(Rounding.ROUND_UP); }
Kip : Yes but I don't own java.math.RoundingMode, so I can't do this in my case. -
Note that there is problem when transfering enum via RMI/IIOP. See this thread:
http://www.velocityreviews.com/forums/t390342-enum-equality.html
0 comments:
Post a Comment