What are enums and why are they useful? What are enums and why are they useful? java java

What are enums and why are they useful?


You should always use enums when a variable (especially a method parameter) can only take one out of a small set of possible values. Examples would be things like type constants (contract status: "permanent", "temp", "apprentice"), or flags ("execute now", "defer execution").

If you use enums instead of integers (or String codes), you increase compile-time checking and avoid errors from passing in invalid constants, and you document which values are legal to use.

BTW, overuse of enums might mean that your methods do too much (it's often better to have several separate methods, rather than one method that takes several flags which modify what it does), but if you have to use flags or type codes, enums are the way to go.

As an example, which is better?

/** Counts number of foobangs. * @param type Type of foobangs to count. Can be 1=green foobangs, * 2=wrinkled foobangs, 3=sweet foobangs, 0=all types. * @return number of foobangs of type */public int countFoobangs(int type)

versus

/** Types of foobangs. */public enum FB_TYPE { GREEN, WRINKLED, SWEET,  /** special type for all types combined */ ALL;}/** Counts number of foobangs. * @param type Type of foobangs to count * @return number of foobangs of type */public int countFoobangs(FB_TYPE type)

A method call like:

int sweetFoobangCount = countFoobangs(3);

then becomes:

int sweetFoobangCount = countFoobangs(FB_TYPE.SWEET);

In the second example, it's immediately clear which types are allowed, docs and implementation cannot go out of sync, and the compiler can enforce this.Also, an invalid call like

int sweetFoobangCount = countFoobangs(99);

is no longer possible.


Why use any programming language feature? The reason we have languages at all is for

  1. Programmers to efficiently and correctly express algorithms in a form computers can use.
  2. Maintainers to understand algorithms others have written and correctly make changes.

Enums improve both likelihood of correctness and readability without writing a lot of boilerplate. If you are willing to write boilerplate, then you can "simulate" enums:

public class Color {    private Color() {} // Prevent others from making colors.    public static final Color RED = new Color();    public static final Color AMBER = new Color();    public static final Color GREEN = new Color();}

Now you can write:

Color trafficLightColor = Color.RED;

The boilerplate above has much the same effect as

public enum Color { RED, AMBER, GREEN };

Both provide the same level of checking help from the compiler. Boilerplate is just more typing. But saving a lot of typing makes the programmer more efficient (see 1), so it's a worthwhile feature.

It's worthwhile for at least one more reason, too:

Switch statements

One thing that the static final enum simulation above does not give you is nice switch cases. For enum types, the Java switch uses the type of its variable to infer the scope of enum cases, so for the enum Color above you merely need to say:

Color color = ... ;switch (color) {    case RED:        ...        break;}

Note it's not Color.RED in the cases. If you don't use enum, the only way to use named quantities with switch is something like:

public Class Color {    public static final int RED = 0;    public static final int AMBER = 1;    public static final int GREEN = 2;}

But now a variable to hold a color must have type int. The nice compiler checking of the enum and the static final simulation is gone. Not happy.

A compromise is to use a scalar-valued member in the simulation:

public class Color {    public static final int RED_TAG = 1;    public static final int AMBER_TAG = 2;    public static final int GREEN_TAG = 3;    public final int tag;    private Color(int tag) { this.tag = tag; }     public static final Color RED = new Color(RED_TAG);    public static final Color AMBER = new Color(AMBER_TAG);    public static final Color GREEN = new Color(GREEN_TAG);}

Now:

Color color = ... ;switch (color.tag) {    case Color.RED_TAG:        ...        break;}

But note, even more boilerplate!

Using an enum as a singleton

From the boilerplate above you can see why an enum provides a way to implement a singleton. Instead of writing:

public class SingletonClass {    public static final void INSTANCE = new SingletonClass();    private SingletonClass() {}    // all the methods and instance data for the class here}

and then accessing it with

SingletonClass.INSTANCE

we can just say

public enum SingletonClass {    INSTANCE;    // all the methods and instance data for the class here}

which gives us the same thing. We can get away with this because Java enums are implemented as full classes with only a little syntactic sugar sprinkled over the top. This is again less boilerplate, but it's non-obvious unless the idiom is familiar to you. I also dislike the fact that you get the various enum functions even though they don't make much sense for the singleton: ord and values, etc. (There's actually a trickier simulation where Color extends Integer that will work with switch, but it's so tricky that it even more clearly shows why enum is a better idea.)

Thread safety

Thread safety is a potential problem only when singletons are created lazily with no locking.

public class SingletonClass {    private static SingletonClass INSTANCE;    private SingletonClass() {}    public SingletonClass getInstance() {        if (INSTANCE == null) INSTANCE = new SingletonClass();        return INSTANCE;    }    // all the methods and instance data for the class here}

If many threads call getInstance simultaneously while INSTANCE is still null, any number of instances can be created. This is bad. The only solution is to add synchronized access to protect the variable INSTANCE.

However, the static final code above does not have this problem. It creates the instance eagerly at class load time. Class loading is synchronized.

The enum singleton is effectively lazy because it's not initialized until first use. Java initialization is also synchronized, so multiple threads can't initialize more than one instance of INSTANCE. You're getting a lazily initialized singleton with very little code. The only negative is the the rather obscure syntax. You need to know the idiom or thoroughly understand how class loading and initialization work to know what's happening.


Besides the already mentioned use-cases, I often find enums useful for implementing the strategy pattern, following some basic OOP guidelines:

  1. Having the code where the data is (that is, within the enum itself -- or often within the enum constants, which may override methods).
  2. Implementing an interface (or more) in order to not bind the client code to the enum (which should only provide a set of default implementations).

The simplest example would be a set of Comparator implementations:

enum StringComparator implements Comparator<String> {    NATURAL {        @Override        public int compare(String s1, String s2) {            return s1.compareTo(s2);        }    },    REVERSE {        @Override        public int compare(String s1, String s2) {            return NATURAL.compare(s2, s1);        }    },    LENGTH {        @Override        public int compare(String s1, String s2) {            return new Integer(s1.length()).compareTo(s2.length());        }    };}

This "pattern" can be used in far more complex scenarios, making extensive use of all the goodies that come with the enum: iterating over the instances, relying on their implicit order, retrieving an instance by its name, static methods providing the right instance for specific contexts etc. And still you have this all hidden behind the interface so your code will work with custom implementations without modification in case you want something that's not available among the "default options".

I've seen this successfully applied for modeling the concept of time granularity (daily, weekly, etc.) where all the logic was encapsulated in an enum (choosing the right granularity for a given time range, specific behavior bound to each granularity as constant methods etc.). And still, the Granularity as seen by the service layer was simply an interface.