Because Java lacks a proper C/C++ enumeration (enum) feature, Java programmers have opted to define simple sets of primitive values:
public class Colors
{
public static final int GREEN = 0;
public static final int RED = 1;
...
}
This is not particularly typesafe, but it works. You can easily copy and serialize these constants, and then use them for
fast switch lookups and so on. In fact, this is how Java language designers originally advised Java programmers to handle Java's lack
of an enumeration feature (see "The Java Language Environment" whitepaper).
The typesafe Java enum concept basically replaces the set of primitive constants above with a set of static final object references encapsulated in a class that (possibly) restricts further instantiation. A basic example would be:
public final class Enum
{
public static final Enum TRUE = new Enum ();
public static final Enum FALSE = new Enum ();
private Enum () {}
} // end of class
Because the set of instances is restricted by the private constructor and Enum class being final, we can assume that Enum.TRUE and Enum.FALSE are the only instances of the Enum class. Thus, we can use the identity comparison (==) operator instead of the equals() method when comparing enum values. The cost of using the == operator equates to directly comparing pointer values in C/C++. Great, right? We have both type and range safety for enum
values while keeping value comparisons efficient.
Alas, the simple Enum class above lacks a few features. One missing feature is that we cannot pass instances of our Enum class as an argument to an RMI (remote method invocation) or EJB (Enterprise JavaBeans) method. To do that, we must mark
the class Serializable:
public final class Enum implements java.io.Serializable
{
public static final Enum TRUE = new Enum ();
public static final Enum FALSE = new Enum ();
private Enum () {}
} // end of class
The above has a subtle trap, as shown here:
ByteArrayOutputStream bout = new ByteArrayOutputStream ();
ObjectOutputStream out = new ObjectOutputStream (bout);
Enum e1 = Enum.TRUE;
out.writeObject (e1);
out.flush ();
ByteArrayInputStream bin = new ByteArrayInputStream (bout.toByteArray ());
ObjectInputStream in = new ObjectInputStream (bin);
Enum e2 = (Enum) in.readObject ();
System.out.println ((e2 == Enum.TRUE || e2 == Enum.FALSE));
This code will print out false, indicating that e2 is neither Enum.TRUE nor Enum.FALSE. This happens because deserializing an object creates a new object without regard to the class's constructors -- the instantiation
protection that we thought we got from making the Enum constructor private doesn't affect deserialization.
== ?By Anonymous on March 12, 2010, 1:21 pmI know this is a really really old article, but why would you EVER assume that you should do an '==' comparison on enums? You don't do it for constant Strings,...
Reply | Read entire comment
Write custom deserialization codeBy Anonymous on December 7, 2009, 6:23 pmThis can be easily solved with custom deserialization code that uses the jvm's existing object during deserialization instead of letting the default deserialization...
Reply | Read entire comment
I'm surprised to see such an article on JavaWorld...By Anonymous on May 18, 2009, 5:22 amI'm surprised to see such an article on JavaWorld. I couldn't disagree more with your statements, especially about how enums are a poor substitutes for static ints....
Reply | Read entire comment
Upcasting typesafe enumsBy Anonymous on December 4, 2008, 7:31 pmOnce the Serialization issue is fixed, typesafe "enum" style classes can be safely compared with == provided the variables are declared as the Enum class, and not...
Reply | Read entire comment
View all comments