Enumeration class - it's really not that simple to use enumeration well!

Posted by ashrust on Tue, 08 Feb 2022 16:50:26 +0100

Watch first and then praise, and form a habit.
Praise collection, brilliant life.

preface

The enum keyword, introduced in Java 5, represents a special type of class that always inherits Java Lang. enum class is special because it is a class type, but it has some special constraints more than class types

1, Overview

In this article, we will see what Java enumerations are, what problems they solve, and how to use Java enumerations to implement some design patterns in practice.

The following example defines the status of a simple enumeration type pizza order. There are three statuses: ordered, ready and delivered:

package shuang.kou.enumdemo.enumtest;

public enum PizzaStatus {
    ORDERED,
    READY, 
    DELIVERED; 
}

In short, we avoid defining constants through the above code. We put all constants related to the status of pizza orders into one enumeration 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.PizzaStatus

2, Custom enumeration method

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

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

3, Use = = to compare enumeration types

Because the enumeration type ensures that there is only one constant instance in the JVM, we can safely use the = = operator to compare two variables, as shown in the above example; 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 will not be raised if both values are null. Conversely, if the equals method is used, a NullPointerException will be thrown:

Pizza.PizzaStatus pizza = null;
System.out.println(pizza.equals(Pizza.PizzaStatus.DELIVERED));//Abnormal null pointer
System.out.println(pizza == Pizza.PizzaStatus.DELIVERED);//normal operation

For compile time security, let's take another example and compare two different enumeration types:

if (Pizza.PizzaStatus.DELIVERED.equals(TestColor.GREEN)); // Normal compilation
if (Pizza.PizzaStatus.DELIVERED == TestColor.GREEN);      // Compilation failed, type mismatch

4, Using enumeration types in switch statements

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

5, Enumerate the properties, methods, and constructors of types

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

Next, let's extend the above example to realize the transition from one stage of pizza to another, and learn how to get rid of the previous if statement and switch statement:

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.
}

The following code shows how it work s:

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

6, Some design patterns are implemented through enumeration

Singleton mode

In general, using classes to implement Singleton pattern is not easy. Enumeration provides a simple way to implement singletons.

Effective Java and Java and patterns highly recommend this method. What are the benefits of using this method to implement enumeration?

<Effective Java>

This method is similar to the public domain method in function, but it is more concise. It provides a serialization mechanism for free and absolutely prevents multiple instantiations, even in the face of complex serialization or reflection attacks. Although this method has not been widely used, the enumeration type of single element has become the best way to implement Singleton- Effective Java Chinese version Second Edition

Java and patterns

In Java and patterns, the author writes that using enumeration to realize single instance control will be more concise, and the serialization mechanism is provided free of charge, and the JVM fundamentally provides guarantee to absolutely prevent multiple instantiation. It is a more concise, efficient and safe way to realize single instance.

The following code snippet shows how to implement singleton mode using enumeration:

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;
    }
}

How to use it? See the following code:

PizzaDeliveryStrategy deliveryStrategy = PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy();
adopt PizzaDeliverySystemConfiguration.getInstance() 
//Get the PizzaDeliverySystemConfiguration of the singleton

Strategy mode

Generally, the policy pattern is implemented by the same interface of different classes.

This means that adding a new policy means adding a new implementation class. This task can be easily accomplished using enumeration, and adding a new implementation means that only another instance with one implementation is defined.

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

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);
}

Add the following methods to Pizza:

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

How to use it? See 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);
}

VII Java 8 and enumeration

The Pizza class can be rewritten in Java 8. You can see how the method lambda and Stream API make the getallundeliveredpizza() and grouppizza bystatus() methods so concise:

getAllUndeliveredPizzas():

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

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;
}

VIII JSON representation of Enum type

Using Jackson library, JSON of enumeration type can be represented as POJO. The following code snippet shows Jackson annotations 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;
    }
}

We can use Pizza and Pizza status as follows:

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

The generated Pizza state is displayed in the following JSON:

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

summary

In this article, we discussed Java enumeration types, from basic knowledge to advanced applications and practical application scenarios. Let's feel the powerful function of enumeration. It's really not so simple to make good use of enumeration classes

The above is the content of this article about enumeration classes

Wait, don't row away. Let the blogger negotiate a meal~

We'll give you some benefits. Bloggers have been promoting Alibaba cloud recently,

Activity discount price: the lowest price in the whole network is 87 yuan / year, 261 yuan / 3 years, which is cheaper than students' 9.9 per month (comfortable one)
New users can try. There is a server of their own, which is very convenient for deployment and learning in the early stage

Alibaba cloud[ Click buy]

White whoring is not good and creation is not easy. Your praise is the biggest driving force of my creation. If I write something wrong, please leave a message in the comment area for correction.
If old fellow is harvested, please give a complimentary complimentary to encourage bloggers.

Topics: Java Design Pattern enum