Java: A platform for platforms
Sun's reorg may seem promising to shareholders but it's also a scramble for position. The question now is whether Sun can, or wants to, maintain its hold on Java technology. Especially with enterprise leaders like SpringSource and RedHat investing heavily in Java's future as a platform for platforms

Also see:

Discuss: Tim Bray on 'What Sun Should Do'

Featured Whitepapers
Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

Java Tip 122: Beware of Java typesafe enumerations

Think twice before relying on instance identity

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Departing from traditional practice for JavaWorld's Tips 'N Tricks column, I will talk about when not to use a previously suggested trick. Specifically, the typesafe enum construct, covered in JDC Tech Tips and other publications, can sometimes be hazardous to your code.

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).

How it's supposed to work

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.

Is that enough?

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


Ok, so what is wrong with that?

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.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources