What are enums and why are they useful?

2018-12-31 09:52发布

Today I was browsing through some questions on this site and I found a mention of an enum being used in singleton pattern about purported thread safety benefits to such solution.

I have never used enums and I have been programing in Java for more than couple a years now. And apparently they changed a lot. Now they even do full blown support of OOP within themselves.

Now why and what for should I use enum in day to day programming?

标签: java enums
23条回答
萌妹纸的霸气范
2楼-- · 2018-12-31 10:20

It is useful to know that enums are just like the other classes with Constant fields and a private constructor.

For example,

public enum Weekday
{
  MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
} 

The compiler compiles it as follows;

class Weekday extends Enum
{
  public static final Weekday MONDAY  = new Weekday( "MONDAY",   0 );
  public static final Weekday TUESDAY = new Weekday( "TUESDAY ", 1 );
  public static final Weekday WEDNESDAY= new Weekday( "WEDNESDAY", 2 );
  public static final Weekday THURSDAY= new Weekday( "THURSDAY", 3 );
  public static final Weekday FRIDAY= new Weekday( "FRIDAY", 4 );
  public static final Weekday SATURDAY= new Weekday( "SATURDAY", 5 );
  public static final Weekday SUNDAY= new Weekday( "SUNDAY", 6 );

  private Weekday( String s, int i )
  {
    super( s, i );
  }

  // other methods...
}
查看更多
谁念西风独自凉
3楼-- · 2018-12-31 10:25

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.

查看更多
浅入江南
4楼-- · 2018-12-31 10:25

Something none of the other answers have covered that make enums particularly powerful are the ability to have template methods. Methods can be part of the base enum and overridden by each type. And, with the behavior attached to the enum, it often eliminates the need for if-else constructs or switch statements as this blog post demonstrates - where enum.method() does what originally would be executed inside the conditional. The same example also shows the use of static imports with enums as well producing much cleaner DSL like code.

Some other interesting qualities include the fact that enums provide implementation for equals(), toString() and hashCode() and implement Serializable and Comparable.

For a complete rundown of all that enums have to offer I highly recommend Bruce Eckel's Thinking in Java 4th edition which devotes an entire chapter to the topic. Particularly illuminating are the examples involving a Rock, Paper, Scissors (i.e. RoShamBo) game as enums.

查看更多
浪荡孟婆
5楼-- · 2018-12-31 10:25

In my opinion, all the answers you got up to now are valid, but in my experience, I would express it in a few words:

Use enums if you want the compiler to check the validity of the value of an identifier.

Otherwise, you can use strings as you always did (probably you defined some "conventions" for your application) and you will be very flexible... but you will not get 100% security against typos on your strings and you will realize them only in runtime.

查看更多
柔情千种
6楼-- · 2018-12-31 10:29

From Java documents -

You should use enum types any time you need to represent a fixed set of constants. That includes natural enum types such as the planets in our solar system and data sets where you know all possible values at compile time—for example, the choices on a menu, command line flags, and so on.

A common example is to replace a class with a set of private static final int constants (within reasonable number of constants) with an enum type. Basically if you think you know all possible values of "something" at compile time you can represent that as an enum type. Enums provide readability and flexibility over a class with constants.

Few other advantages that I can think of enum types. They is always one instance of a particular enum class (hence the concept of using enums as singleton arrives). Another advantage is you can use enums as a type in switch-case statement. Also you can use toString() on the enum to print them as readable strings.

查看更多
何处买醉
7楼-- · 2018-12-31 10:29

ENum stands for "Enumerated Type". It is a data type having a fixed set of constants which you define yourself.

查看更多
登录 后发表回答