Recently review Java enumeration, see this article feel good, so simple translation and improve some content, share with you, I hope you can also gain. Also, don’t forget to add something at the end!

Ps: here sent an enumeration article, but also because the back to send a very practical about SpringBoot global exception handling better practice, which used enumeration.

This article translated by JavaGuide, public number: JavaGuide, the original address: https://www.baeldung.com/a-guide-to-java-enums.

Please indicate the above paragraph of text.

1. An overview

In this article, we’ll look at what Java enumerations are, what problems they solve, and how to use Java enumerations to implement some design patterns in practice.

The enum keyword was introduced in Java5 to represent a special type of class that always inherits from the java.lang. enum class, more on which can be found in the official documentation.

Enumerations are often compared to constants, perhaps because they are used in place of constants. So what are the advantages of this approach?

Constants defined in this way make code more readable, allow compile-time checking, pre-record lists of acceptable values, and avoid unexpected behavior due to invalid values being passed in.

The following example defines a simple enumeration of pizza order states, which are ORDERED, READY, and DELIVERED:

package shuang.kou.enumdemo.enumtest;

public enum PizzaStatus {
    ORDERED,
    READY, 
    DELIVERED; 
}Copy the code

In short, we avoided defining constants by using the code above, placing all the constants associated with the pizza order’s state into an enumerated type.

System.out.println(PizzaStatus.ORDERED.name()); //ORDERED System.out.println(PizzaStatus.ORDERED); //ORDERED System.out.println(PizzaStatus.ORDERED.name().getClass()); //class java.lang.String System.out.println(PizzaStatus.ORDERED.getClass()); //class shuang.kou.enumdemo.enumtest.PizzaStatusCopy the code

2. Customize enumeration methods

Now that we have a basic understanding of what enumerations are and how to use them, let’s take the previous example to the next level by defining some additional API methods on enumerations:

public class Pizza {
    private PizzaStatus status;
    public enum PizzaStatus {
        ORDERED,
        READY,
        DELIVERED;
    }
 
    public boolean isDeliverable() {
        if (getStatus() == PizzaStatus.READY) {
            return true;
        }
        return false;
    }
     
    // Methods that set and get the status variable.
}Copy the code

3. Use == to compare enumerated types

Because enumeration types ensure that only one constant instance exists in the JVM, we can safely compare two variables using the “==” operator, as shown in the example above; In addition, the “==” operator provides compile-time and run-time security.

First, let’s look at runtime security in the following code snippet, where the “==” operator is used to compare states and NullPointerException is not raised if both values are null. Instead, if equals is used, NullPointerException is thrown:

if(testPz.getStatus().equals(Pizza.PizzaStatus.DELIVERED)); 
if(testPz.getStatus() == Pizza.PizzaStatus.DELIVERED); Copy the code

For compile-time security, let’s look at another example where two different enumeration types are compared, and the equal method is used to determine that the result is true because the enumeration value of the getStatus method is the same as the enumeration value of the other type, but logically it should be false. This problem can be avoided using the == operator. Because the compiler will indicate type incompatibility errors:

if(testPz.getStatus().equals(TestColor.GREEN));
if(testPz.getStatus() == TestColor.GREEN);Copy the code

4. Use enumerated types in switch statements

public int getDeliveryTimeInDays() {
    switch (status) {
        case ORDERED: return 5;
        case READY: return 2;
        case DELIVERED: return 0;
    }
    return 0;
}Copy the code

5. Enumerate type properties, methods, and constructors

I (JavaGuide) added at the end of the article.

You can make it more powerful by defining properties, methods, and constructors in enumerated types.

Now, let’s extend the above example to transition from one phase of pizza to another and see how to get rid of the if and switch statements we used earlier:

public class Pizza { private PizzaStatus status; public enum PizzaStatus { ORDERED (5){ @Override public boolean isOrdered() { return true; } }, READY (2){ @Override public boolean isReady() { return true; } }, DELIVERED (0){ @Override public boolean isDelivered() { return true; }}; private int timeToDelivery; public boolean isOrdered() {return false; } public boolean isReady() {return false; } public boolean isDelivered(){return false; } public int getTimeToDelivery() { return timeToDelivery; } PizzaStatus (int timeToDelivery) { this.timeToDelivery = timeToDelivery; } } public boolean isDeliverable() { return this.status.isReady(); } public void printTimeToDeliver() { System.out.println("Time to delivery is " + this.getStatus().getTimeToDelivery()); } // Methods that set and get the status variable. }Copy the code

The following code shows how it works:

@Test
public void givenPizaOrder_whenReady_thenDeliverable() {
    Pizza testPz = new Pizza();
    testPz.setStatus(Pizza.PizzaStatus.READY);
    assertTrue(testPz.isDeliverable());
}Copy the code

6.EnumSet and EnumMap

6.1. Enumsets

EnumSet is a Set type designed specifically for enumeration types.

Compared to HashSet, it is a very efficient and compact representation of a specific set of Enum constants because of its internal bitvector representation.

It provides a type-safe alternative to the traditional int-based “bit flags,” allowing us to write concise code that is easier to read and maintain.

EnumSet is an abstract class that has two implementations: RegularEnumSet and JumboEnumSet. The choice depends on the number of constants in the enumeration at instantiation time.

EnumSet is a good use for enumerating constant sets in many scenarios (such as subsets, additions, deletions, containsAll, and removeAll batch operations). Use Enum. Values () if you need to iterate over all possible constants.

public class Pizza {
 
    private static EnumSet<PizzaStatus> undeliveredPizzaStatuses =
      EnumSet.of(PizzaStatus.ORDERED, PizzaStatus.READY);
 
    private PizzaStatus status;
 
    public enum PizzaStatus {
        ...
    }
 
    public boolean isDeliverable() {
        return this.status.isReady();
    }
 
    public void printTimeToDeliver() {
        System.out.println("Time to delivery is " + 
          this.getStatus().getTimeToDelivery() + " days");
    }
 
    public static List<Pizza> getAllUndeliveredPizzas(List<Pizza> input) {
        return input.stream().filter(
          (s) -> undeliveredPizzaStatuses.contains(s.getStatus()))
            .collect(Collectors.toList());
    }
 
    public void deliver() { 
        if (isDeliverable()) { 
            PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy()
              .deliver(this); 
            this.setStatus(PizzaStatus.DELIVERED); 
        } 
    }
     
    // Methods that set and get the status variable.
}Copy the code

The following tests demonstrate EnumSet’s power in some scenarios:

@Test
public void givenPizaOrders_whenRetrievingUnDeliveredPzs_thenCorrectlyRetrieved() {
    List<Pizza> pzList = new ArrayList<>();
    Pizza pz1 = new Pizza();
    pz1.setStatus(Pizza.PizzaStatus.DELIVERED);
 
    Pizza pz2 = new Pizza();
    pz2.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz3 = new Pizza();
    pz3.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz4 = new Pizza();
    pz4.setStatus(Pizza.PizzaStatus.READY);
 
    pzList.add(pz1);
    pzList.add(pz2);
    pzList.add(pz3);
    pzList.add(pz4);
 
    List<Pizza> undeliveredPzs = Pizza.getAllUndeliveredPizzas(pzList); 
    assertTrue(undeliveredPzs.size() == 3); 
}Copy the code

6.2. EnumMap

EnumMap is a specialized mapping implementation that uses enumeration constants as keys. It is an efficient and compact implementation compared to the corresponding HashMap, and is internally represented as an array:

EnumMap<Pizza.PizzaStatus, Pizza> map;Copy the code

Let’s take a quick look at a real-world example that shows how it can be used in practice:

public static EnumMap<PizzaStatus, List<Pizza>> 
  groupPizzaByStatus(List<Pizza> pizzaList) {
    EnumMap<PizzaStatus, List<Pizza>> pzByStatus = 
      new EnumMap<PizzaStatus, List<Pizza>>(PizzaStatus.class);
     
    for (Pizza pz : pizzaList) {
        PizzaStatus status = pz.getStatus();
        if (pzByStatus.containsKey(status)) {
            pzByStatus.get(status).add(pz);
        } else {
            List<Pizza> newPzList = new ArrayList<Pizza>();
            newPzList.add(pz);
            pzByStatus.put(status, newPzList);
        }
    }
    return pzByStatus;
}Copy the code

The following tests demonstrate the power of EnumMap in some scenarios:

@Test
public void givenPizaOrders_whenGroupByStatusCalled_thenCorrectlyGrouped() {
    List<Pizza> pzList = new ArrayList<>();
    Pizza pz1 = new Pizza();
    pz1.setStatus(Pizza.PizzaStatus.DELIVERED);
 
    Pizza pz2 = new Pizza();
    pz2.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz3 = new Pizza();
    pz3.setStatus(Pizza.PizzaStatus.ORDERED);
 
    Pizza pz4 = new Pizza();
    pz4.setStatus(Pizza.PizzaStatus.READY);
 
    pzList.add(pz1);
    pzList.add(pz2);
    pzList.add(pz3);
    pzList.add(pz4);
 
    EnumMap<Pizza.PizzaStatus,List<Pizza>> map = Pizza.groupPizzaByStatus(pzList);
    assertTrue(map.get(Pizza.PizzaStatus.DELIVERED).size() == 1);
    assertTrue(map.get(Pizza.PizzaStatus.ORDERED).size() == 2);
    assertTrue(map.get(Pizza.PizzaStatus.READY).size() == 1);
}Copy the code

7. Implement some design patterns through enumeration

7.1 Singleton Mode

In general, it is not easy to implement the Singleton pattern using classes, and enumerations provide an easy way to implement singletons.

This approach is highly recommended by Effective Java and Java & Patterns. What are the benefits of implementing enumerations this way?

“Effective Java”

This approach is functionally similar to the public domain approach, but it is more concise, provides a serialization mechanism for free, and absolutely prevents multiple instantiations, even in the face of complex serialization or reflection attacks. Although this approach is not yet widely adopted, enumerations of single-element types have emerged as the best way to implement Singleton. — Effective Java Chinese Edition 2

Java and Patterns

In Java and Patterns, the authors write that using enumerations to implement singleton control is simpler, provides a serialization mechanism for free, and is fundamentally guaranteed by the JVM to absolutely prevent multiple instances. It is a simpler, more efficient, and safer way to implement singleton control.

The following code snippet shows how to implement the singleton pattern using enumerations:

public enum PizzaDeliverySystemConfiguration {
    INSTANCE;
    PizzaDeliverySystemConfiguration() {
        // Initialization configuration which involves
        // overriding defaults like delivery strategy
    }
 
    private PizzaDeliveryStrategy deliveryStrategy = PizzaDeliveryStrategy.NORMAL;
 
    public static PizzaDeliverySystemConfiguration getInstance() {
        return INSTANCE;
    }
 
    public PizzaDeliveryStrategy getDeliveryStrategy() {
        return deliveryStrategy;
    }
}Copy the code

How do you use it? Take a look at the following code:

PizzaDeliveryStrategy deliveryStrategy = PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy();Copy the code

Through PizzaDeliverySystemConfiguration. GetInstance () get PizzaDeliverySystemConfiguration is singleton

7.2 Policy Mode

Typically, policy patterns are implemented by different classes implementing the same interface.

This means that adding new policies means adding new implementation classes. Using enumerations, you can do this easily, and adding a new implementation means defining only one other instance of an implementation.

The following code snippet shows how to implement the policy pattern using enumerations:

public enum PizzaDeliveryStrategy { EXPRESS { @Override public void deliver(Pizza pz) { System.out.println("Pizza will be delivered in express mode"); } }, NORMAL { @Override public void deliver(Pizza pz) { System.out.println("Pizza will be delivered in normal mode"); }}; public abstract void deliver(Pizza pz); }Copy the code

Add the following to Pizza:

public void deliver() { if (isDeliverable()) { PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy() .deliver(this); this.setStatus(PizzaStatus.DELIVERED); }}Copy the code

How do you use it? Take a look at the following code:

@Test
public void givenPizaOrder_whenDelivered_thenPizzaGetsDeliveredAndStatusChanges() {
    Pizza pz = new Pizza();
    pz.setStatus(Pizza.PizzaStatus.READY);
    pz.deliver();
    assertTrue(pz.getStatus() == Pizza.PizzaStatus.DELIVERED);
}Copy the code

Java 8 and enumerations

The Pizza class can be rewritten in Java 8, and you can see how the method lambda and Stream API make the getAllUndeliveredPizzas () and groupPizzaByStatus () methods so concise:

GetAllUndeliveredPizzas () :

public static List<Pizza> getAllUndeliveredPizzas(List<Pizza> input) { return input.stream().filter( (s) -> ! deliveredPizzaStatuses.contains(s.getStatus())) .collect(Collectors.toList()); }Copy the code

GroupPizzaByStatus () :

public static EnumMap<PizzaStatus, List<Pizza>> 
  groupPizzaByStatus(List<Pizza> pzList) {
    EnumMap<PizzaStatus, List<Pizza>> map = pzList.stream().collect(
      Collectors.groupingBy(Pizza::getStatus,
      () -> new EnumMap<>(PizzaStatus.class), Collectors.toList()));
    return map;
}Copy the code

9. JSON representation of the Enum type

Using the Jackson library, JSON of enumerated types can be represented as POJOs. The following code snippet shows the Jackson annotation that can be used for the same purpose:

@JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum PizzaStatus { ORDERED (5){ @Override public boolean isOrdered()  { return true; } }, READY (2){ @Override public boolean isReady() { return true; } }, DELIVERED (0){ @Override public boolean isDelivered() { return true; }}; private int timeToDelivery; public boolean isOrdered() {return false; } public boolean isReady() {return false; } public boolean isDelivered(){return false; } @JsonProperty("timeToDelivery") public int getTimeToDelivery() { return timeToDelivery; } private PizzaStatus (int timeToDelivery) { this.timeToDelivery = timeToDelivery; }}Copy the code

We can use Pizza and PizzaStatus as follows:

Pizza pz = new Pizza();
pz.setStatus(Pizza.PizzaStatus.READY);
System.out.println(Pizza.getJsonString(pz));Copy the code

Generate the Pizza state as shown in JSON below:

{
  "status" : {
    "timeToDelivery" : 2,
    "ready" : true,
    "ordered" : false,
    "delivered" : false
  },
  "deliverable" : true
}Copy the code

For more information on JSON serialization/deserialization of enumerated types, including customization, see Jackson- Serializing Enumerations to JSON Objects.

10. Summary

In this article, we discussed Java enumeration types, from the basics to advanced applications and actual application scenarios, so that we can feel the power of enumeration.

Added 11.

As we mentioned above, we can make it more powerful by defining properties, methods, and constructors in enumerated types.

Let me show you through a practical example that there are several different uses when we call the SMS verification code, which we define as follows:

Public enum PinType {REGISTER(100000, "REGISTER "), FORGET_PASSWORD(100001," FORGET_PASSWORD "), UPDATE_PHONE_NUMBER(100002, "Update mobile phone number usage "); private final int code; private final String message; PinType(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } @Override public String toString() { return "PinType{" + "code=" + code + ", message='" + message + '\'' + '}'; }}Copy the code

Actual use:

System.out.println(PinType.FORGET_PASSWORD.getCode());
System.out.println(PinType.FORGET_PASSWORD.getMessage());
System.out.println(PinType.FORGET_PASSWORD.toString());Copy the code

Output:

100001 Forget password use PinType{code=100001, message=' Forget password use '}Copy the code

In this case, the actual use will be very flexible and convenient!

Open Source Project Recommendation

Other open source projects recommended by the authors:

  1. JavaGuide: A Java learning + Interview Guide that covers the core knowledge that most Java programmers need to master.
  2. Springboot-guide: A Spring Boot tutorial for beginners and experienced developers.
  3. Advancer-advancement: I think there are some good habits that technical people should have!
  4. Spring-security-jwt-guide: Start from Scratch! Spring Security With JWT (including permission validation) backend part of the code.

The public,