What are enums and why are they useful?

后端 未结 27 1308
一整个雨季
一整个雨季 2020-11-22 07:06

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

相关标签:
27条回答
  • 2020-11-22 07:40

    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.

    0 讨论(0)
  • 2020-11-22 07:41

    There are many answers here, just want to point two specific ones:

    1) Using as constants in Switch-case statement. Switch case won't allow you to use String objects for case. Enums come in handy. More: http://www.javabeat.net/2009/02/how-to-use-enum-in-switch/

    2) Implementing Singleton Design Pattern - Enum again, comes to rescue. Usage, here: What is the best approach for using an Enum as a singleton in Java?

    0 讨论(0)
  • 2020-11-22 07:42

    Enum? Why should it be used? I think it's more understood when you will use it. I have the same experience.

    Say you have a create, delete, edit and read database operation.

    Now if you create an enum as an operation:

    public enum operation {
        create("1")
        delete("2")
        edit("3")
        read("4")
    
        // You may have is methods here
        public boolean isCreate() {
            return this.equals(create);
        }
        // More methods like the above can be written
    
    }
    

    Now, you may declare something like:

    private operation currentOperation;
    
    // And assign the value for it 
    currentOperation = operation.create
    

    So you can use it in many ways. It's always good to have enum for specific things as the database operation in the above example can be controlled by checking the currentOperation. Perhaps one can say this can be accomplished with variables and integer values too. But I believe Enum is a safer and a programmer's way.

    Another thing: I think every programmer loves boolean, don't we? Because it can store only two values, two specific values. So Enum can be thought of as having the same type of facilities where a user will define how many and what type of value it will store, just in a slightly different way. :)

    0 讨论(0)
提交回复
热议问题