Skip to main content

A short primer on Java enums - part 1

Posted by johnsmart on May 14, 2008 at 6:23 PM PDT

In his JavaOne talk this year, Josh Bloch gave some very useful tips about using enums in Java. Here is my take on enums, and how to use them to represent simple value lists which would otherwise be stored in code tables. This document is in two parts - Part one covers the basics of Java enums, and Part 2 goes into more advanced use cases such as using enums with Hibernate.

Introducing Java enums

An enum represents a pre-defined list of static values. Most applications have lots of places where you need this sort of thing. Colours, states, font-styles - the list is endless. A common older approach (required before Java 5) was to define lists of static values is to use integer constants, as shown here:

public class Issue {
    public static final int STATUS_OPEN = 1;
    public static final int STATUS_CLOSED = 2;
    public static final int STATUS_REOPENED = 3;
   
    private int status;

    public void setStatus(int status) {
        this.status = status
    }
    ...
}

However, this is a very poor solution, for many reasons, that Josh discusses in some detail in his Effective Java book. For example, there is no type safety. You can use any integer value in this field, which can result in errors, eg:

    issue.setStatus(4); // This is wrong!

In addition, the solution is fragile. These values are integers, so they will be compiled into the classes that use them. Should you modify the int value associated with a particular constant, the client classes will not automatically recompile. And if the client classes are not recompiled, they will continue to use the old values, which will result in strange errors.

Luckely, in Java 5, you can use enums for a much more type-safe approach. An enum is basically a type-safe, pre-defined list of values, which you can define as follows:

public enum Status {
    Open, Closed, Reopened
};

You can define an enum in the same way as you would a class. Enums that are used by many classes should be defined as public, stand-alone classes, defined in their own *.java file. Enums that are only used by a particular class, on the other hand should be defined as public inner classes, as shown here:

public class Issue {

    public enum Status {
        Open, Closed, Reopened
    };

    private Status status;

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }
    ...
}

On of the great things about this is how typesafe it all is. You can use an enum like this as follows:

        Issue issue = new Issue();
        issue.setStatus(Issue.Status.Closed);
        assertThat(issue.getStatus(), is(Issue.Status.Closed));

Here, you cannot fail to put a legal value into the status field, as it is verified by the compiler. For example, the following code, where we try to assign an Importance value to the Status field, simply won't compile:

        Issue issue = new Issue();
        activity.setStatus(Issue.Importance.Low);  // This will not compile

Of course, if you use static imports, the code is even simpler:

import static com.mycompany.Issue.Status.Closed;
        ...
        Issue issue = new Issue();
        issue.setStatus(Closed);

Listing enum values

Now there are times that you might need to list the values in an enumaration. For example, you may want to create a list of entries in a SELECT element on an HTML page. You can do this with the static values() method, as shown here:

        ActivityStatus[] values = ActivityStatus.values();
        for(ActivityStatus status : values) {
            System.out.println(status);
        }

This will produce the following list:

Open
Closed
Reopened

Easy! Note that the enum values are displayed in their natural order (their order of appearance in the Java class), not in alphabetical order. This means you can define an implicit order, and not worry about having to add an extra field to define the order manually.

Conclusion

So now, we've covered some of the basic uses of enums. In the next part of this article, we will look at more advanced uses of enums, such as how they integrate with Hibernate.

Comments

Fantastic tutorial. I would also suggest to read through ...

Fantastic tutorial. I would also suggest to read through following comprehensive 15 Java Enum Interview Questions. It provide good overview of different enum features.

Similar to tusca, but still avoiding the IllegalArgumentException, and avoiding a switch statement, you can use this syntax: public enum Status { OPEN { boolean isOpen() {return true; } }, CLOSED { boolean isOpen() {return false; } }, REOPENED { boolean isOpen() {return true; } }; abstract boolean isOpen(); } This syntax is based off of (near the bottom of the page, see the Operation enum) http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html

Indeed Enum are more versatile than one can think of , see ...

Indeed Enum are more versatile than one can think of , see the below post 10 examples of enum in java to know what else can you do with enum in java.

if the Open status and the ReOpened status both mean that the status "is Open" then maybe add that meaning to the enum itself, and use isOpen(). public class Issue { public enum Status { Open(true), Closed(false), Reopened(true); private boolean open; private Status(boolean open) { this.open=open; } public boolean isOpen() { return open; } }; private Status status; public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; } public boolean isOpen() { return this.status.isOpen(); } }

re bironran Wouldn't that could be simpler like this: public boolean getStatusSimple(Status status) { assert(status != null); switch (status) { case CLOSED: return true; case OPEN: return false; case REOPENED: return true; default: throw new IllegalArgumentException("Invalid Status"); } } Although, I'm surprised you need the assert. I would have handled null status cases in code well before we got to a method like this. Just like you did with your int case. I would think it's fair to assume that Status can only be NonNull. But.. I guess depending on your feelings on that JSR (what was it, 308 or something) - A @NonNullable annotation or such could be added and then you can take the assert out.

Java enums are great, but switching on them is painful.
When receiving an int you could use the following syntax: public boolean getStatus1(int status) { switch (status) { case STATUS_OPEN: return true; case STATUS_CLOSED: return false; case STATUS_REOPENED: return true; } throw new IllegalArgumentException("status must be valild"); } which is all well and nice.
However, when you move to enums, you first code it like this (at least I did): public boolean getStatus2(Status status) { switch (status) { case CLOSED: return true; case OPEN: return false; case REOPENED: return true; } } Oh what a surprise - this doesn't even compile!
So you add a "throw new IllegalArgumentException("status must be valid");" after the switch, compile it and run it.
...
and get an NPE for all your effort.
You revise the code again to this: public boolean getStatus3(Status status) { if(status != null) { throw new IllegalArgumentException("status may not be null"); } switch (status) { case CLOSED: return true; case OPEN: return false; case REOPENED: return true; } throw new IllegalArgumentException("status must be valid"); } Compared with the first "int" code, I'd say the final enum version has about 100% LOC overhead.

Now, enums can't be extended, can't be effectively type-casted (because they extend Object and nothing can extend them). However, for some reason, they can be null and, maybe even worse, the compiler thinks they can be of other values than stated at compile time (which is true, if you compile with one version and run with another, but that's true for all classes).

This leads to a huge overhead above what you had to write when naively using "int" as the enum, not to mention "int" let you combine flags (assuming you using the binary notation) and there is no such option for enums (other then create a set of enums - Set in your example - and iterating over it), which adds even more overhead to the switching method.

All in all, I like enums, but hate the way they're implemented.

Get a Gateway.

One of the most interesting parts of his talk, I thought, was his use of an enum with one element as a way of providing a serializable singleton.