Are we the same?

Estimate a lot of small partners (including myself) have this situation, in the self-study Java language reading, about enumeration enum this knowledge point may be a little “underestimate the enemy”, think this content is very simple, and do not pay attention to the use of code in the actual process.

Yes, me too! It wasn’t until one day that I failed the code review and was criticized by the technical director that I picked up Java Programming Ideas and looked at enumeration again.


Why enumeration is needed

Does the constant define that it doesn’t smell? Why enumeration?

For example, take the video uploaded by station B as an example. Generally, the video has three states: draft, review and release, which can be defined as static constants:

public class VideoStatus {
    
    public static final int Draft = 1; The draft / /
    
    public static final int Review = 2; / / audit
    
    public static final int Published = 3; / / release
}
Copy the code

Static constant definitions of single-valued types are also true, mainly because there is no explicit constraint on where they are used, such as:

void judgeVideoStatus( int status ) {... }Copy the code

For example, the judgeVideoStatus function here is intended to be one of the three static constants passed to VideoStatus, but since there is no type constraint, it is ok to pass in any int value without any warning from the compiler.

But with enumerations, the above situation can be strictly constrained by enumerations. For example, using enumerations to define video state is very concise:

public enum VideoStatus {
    Draft, Review, Published
}
Copy the code

And there are stronger type constraints mainly where enumerations are used:

// The input parameter has an explicit type constraint
void judgeVideoStatus( VideoStatus status ) {... }Copy the code

This way, when using judgeVideoStatus, the input parameter type is explicitly type bound and the compiler checks for any invalid value passed in to avoid potential problems.

In addition, enumerations are more convenient and elegant in terms of extensibility than general constants.


Reintroduce the system to enumerations

For example, in a backend management system, there must be a user role, and the role is usually fixed, suitable to define as an enumeration:

public enum UserRole {

    ROLE_ROOT_ADMIN,  // System administrator

    ROLE_ORDER_ADMIN, // Order manager

    ROLE_NORMAL       // Common user
}
Copy the code

Let’s use this UserRole as an example to illustrate all the basic uses of enumeration:

UserRole role1 = UserRole.ROLE_ROOT_ADMIN;
UserRole role2 = UserRole.ROLE_ORDER_ADMIN;
UserRole role3 = UserRole.ROLE_NORMAL;

// values() method: returns an array of all enumerated constants
for ( UserRole role : UserRole.values() ) {
    System.out.println(role);
}
/ / print:
// ROLE_ROOT_ADMIN
// ROLE_ORDER_ADMIN
// ROLE_NORMAL

// ordinal() method: returns the ordinal of the enumeration constant, starting from 0
System.out.println( role1.ordinal() ); / / print 0
System.out.println( role2.ordinal() ); / / print 1
System.out.println( role3.ordinal() ); / / print 2

// compareTo() method: enumerate comparisons between constants
System.out.println( role1.compareTo(role2) ); / / print - 1
System.out.println( role2.compareTo(role3) ); / / print - 2
System.out.println( role1.compareTo(role3) ); / / print - 2

// Name () method: get the name of the enumeration constant
System.out.println( role1.name() ); / / print ROLE_ROOT_ADMIN
System.out.println( role2.name() ); / / print ROLE_ORDER_ADMIN
System.out.println( role3.name() ); / / print ROLE_NORMAL

// valueOf() method: returns an enumeration constant with the specified name
System.out.println( UserRole.valueOf( "ROLE_ROOT_ADMIN")); System.out.println( UserRole.valueOf("ROLE_ORDER_ADMIN")); System.out.println( UserRole.valueOf("ROLE_NORMAL"));Copy the code

In addition, enumerations can be used in switch statements, and the meaning is more explicit:

UserRole userRole = UserRole.ROLE_ORDER_ADMIN;
switch (userRole) {
    case ROLE_ROOT_ADMIN:  // better than 1,2,3!
        System.out.println("This is the system administrator role.");
        break;
    case ROLE_ORDER_ADMIN:
        System.out.println("This is the order manager role.");
        break;
    case ROLE_NORMAL:
        System.out.println("This is a normal user role.");
        break;
}
Copy the code

Custom extended enumeration

The enumeration example shown above is very simple and is a single-value case, whereas real projects tend to use enumerations with multiple values.

For example, I would like to expand the UserRole enumeration above to include the role name — the role encoding correspondence, which is also commonly used in real projects.

At this point we can customize various properties, constructors, and even methods in enumerations:

public enum UserRole {

    ROLE_ROOT_ADMIN( "System Administrator".000000 ),
    ROLE_ORDER_ADMIN( "Order Manager".100000 ),
    ROLE_NORMAL( "Ordinary user".200000),;// The following are custom attributes
    
    private final String roleName;  // Role name

    private final Integer roleCode; // Character encoding

    // The following are custom constructors
    
    UserRole( String roleName, Integer roleCode ) {
        this.roleName = roleName;
        this.roleCode = roleCode;
    }

    // The following are custom methods
    
    public String getRoleName(a) {
        return this.roleName;
    }

    public Integer getRoleCode(a) {
        return this.roleCode;
    }

    public static Integer getRoleCodeByRoleName( String roleName ) {
        for( UserRole enums : UserRole.values() ) {
            if( enums.getRoleName().equals( roleName ) ) {
                returnenums.getRoleCode(); }}return null; }}Copy the code

As you can see from the above code, you can declare properties, constructors, and member methods in enum classes just as you would in a normal Class.


Enumeration + interface =?

For example, when I talked about annoying if/else elimination in my previous article, “Promise me no more if/else”, I talked about how this can be done easily by letting enumerations implement interfaces.

Here’s a place to review again:

There is an obvious correspondence between what roles can do, so we first define a common interface RoleOperation that represents the operations that different roles can do:

public interface RoleOperation {
    String op(a);  // Indicates what op operations a role can do
}
Copy the code

Let’s leave all the different roles to enumeration classes, define an enumeration class RoleEnum, and let it implement the RoleOperation interface:

public enum RoleEnum implements RoleOperation {

    // System administrator (with user A permission)
    ROLE_ROOT_ADMIN {
        @Override
        public String op(a) {
            return "ROLE_ROOT_ADMIN:" + " has AAA permission"; }},// Order manager (with B operation permission)
    ROLE_ORDER_ADMIN {
        @Override
        public String op(a) {
            return "ROLE_ORDER_ADMIN:" + " has BBB permission"; }},// Common user (with C operation permission)
    ROLE_NORMAL {
        @Override
        public String op(a) {
            return "ROLE_NORMAL:" + " has CCC permission"; }}; }Copy the code

This makes the call surprisingly simple, with one line of code and no if/else at all:

public class JudgeRole {
    public String judge( String roleName ) {
        // One line of code done! The if/else goes up in smoke
        returnRoleEnum.valueOf(roleName).op(); }}Copy the code

And that way, if I want to extend the conditions in the future, I just need to add code to the enumeration class instead of changing any old code, very much in line with the open closed principle!


Enumeration and design patterns

What? Can enumerations also implement design patterns?

Yes! Not only can but several can be implemented!

1. Singleton mode

public class Singleton {

    // Privatize the constructor to avoid external instance creation
    private Singleton(a) {}// Define an internal enumeration
    public enum SingletonEnum{

        SEED;  // Only enumeration object, we call it "seed player"!

        private Singleton singleton;

        SingletonEnum(){
            singleton = new Singleton(); // The real object creation is hidden here!
        }

        public Singleton getInstnce(a){
            returnsingleton; }}// The object fetching method is deliberately exposed and is also the only entry to the external fetching instance
    public static Singleton getInstance(a){
        return SingletonEnum.SEED.getInstnce(); // Do it by enumerating the seeded players}}Copy the code

2. Strategy mode

This is also a good example, for example, to write an addition, subtraction, multiplication and division calculator based on the policy pattern using enumeration

public class Test {

    public enum Calculator {

        ADDITION {
            public Double execute( Double x, Double y ) {
                return x + y; / / add
            }
        },

        SUBTRACTION {
            public Double execute( Double x, Double y ) {
                return x - y; / / subtraction
            }
        },

        MULTIPLICATION {
            public Double execute( Double x, Double y ) {
                return x * y; / / the multiplication
            }
        },


        DIVISION {
            public Double execute( Double x, Double y ) {
                return x/y;  / / division}};public abstract Double execute(Double x, Double y);
    }
    
    public static void main(String[] args) {
        System.out.println( Calculator.ADDITION.execute( 4.0.2.0));/ / print 6.0
        System.out.println( Calculator.SUBTRACTION.execute( 4.0.2.0));/ / print 2.0
        System.out.println( Calculator.MULTIPLICATION.execute( 4.0.2.0));/ / print 8.0
        System.out.println( Calculator.DIVISION.execute( 4.0.2.0));/ / print 2.0}}Copy the code

A collection class dedicated to enumeration

We are used to using collections such as HashMap and HashSet to hold elements, and there are special collection classes for enumerations: EnumSet and EnumMap

1, enumsets

EnumSet is a Set type designed specifically to hold enumeration types.

For example, consider the role enumeration defined at the beginning of this article:

public enum UserRole {

    ROLE_ROOT_ADMIN,  // System administrator

    ROLE_ORDER_ADMIN, // Order manager

    ROLE_NORMAL       // Common user
}
Copy the code

For example, a group of people come into the system and we need to check whether they are one of the roles:

// Define an exclusive set of administrator roles
EnumSet<UserRole> userRolesForAdmin 
    = EnumSet.of( 
        UserRole.ROLE_ROOT_ADMIN,
        UserRole.ROLE_ORDER_ADMIN 
    );

// Check whether an incoming user is an administrator
Boolean isAdmin( User user ) {
    
    if( userRoles.contains( user.getUserRole() ) )
        return true;
    
    return false;
}
Copy the code

2, EnumMap

Similarly, EnumMap is used to hold Map types whose enumeration type is key.

For example, a group of people come into the system and we need to count how many people there are in different roles:

Map<UserRole,Integer> userStatisticMap = new EnumMap<>(UserRole.class);

for ( User user : userList ) {
    Integer num = userStatisticMap.get( user.getUserRole() );
    if( null! = num ) { userStatisticMap.put( user.getUserRole(), num+1 );
    } else {
        userStatisticMap.put( user.getUserRole(), 1); }}Copy the code

EnumMap is pretty handy.


Total knot

Small enumeration to play so many tricks, but fortunately the process of exploration and summary is very interesting, also reviewed a lot of knowledge, take your time.