In the last blog post [Enum] detailed explanation of enumeration classes in Java (Part 1) , just a simple definition of enumeration types. That is, when defining enumeration types, only enumeration instances are defined, and member variables and methods are not defined. In fact, enumeration classes can do this. In other words, the enumeration class can be regarded as a regular class, except that inheritance cannot be used (the compiler will automatically inherit the Enum class for us). Next, let's look at other uses of enumeration classes~~
1. Construction method of enumeration class
Enumeration classes can have constructors. Constructors are private modifiers by default, and can only be private. Because the instance of enumeration class cannot be created by the outside world! So why? (why is the private modification? Why can't instances of enumerated classes be created by the outside world?)
In the JVM, in order to ensure the uniqueness of enumeration instances in each enumeration class, external new is not allowed (to prevent users from generating instances and destroying uniqueness), so the constructor will be designed as private. That is, enumeration is designed as singleton mode in JDK.
So, when will enumeration objects be instantiated?
The JVM helps us instantiate enumeration types when they are loaded (you can instantiate as many as you define in the enumeration class). The JVM specification clearly stipulates that when loading classes, the JVM will ensure the safety of threads, that is, when loading instantiated enumeration types, it will ensure the uniqueness of enumerated instances!!
Custom construction method
Customize an enumeration class ColorEnum, which has three enumeration instances. For example:
public enum ColorEnum { RED , GREEN , BLUE ; // Custom construction method ColorEnum() { System.out.println("Constructor called"); } }
A parameterless constructor is customized in the enumeration class ColorEnum. Creating an enumeration instance is equivalent to calling the parameter constructor of this class.
Therefore, three instances will call the constructor three times and print "constructor called" three times.
To customize the construction method, pay attention to the following two points:
- Whether a user-defined construction method takes parameters depends on the enumeration instance. If the enumeration instance takes parameters (see below), the construction method takes parameters; Otherwise, you cannot take parameters
- Do not manually add the private modifier before customizing the constructor. The compiler will automatically add it for us
2. Member variables in enumeration
As mentioned before, enumeration classes are the same as normal classes. Then, you can have instance variables, instance methods, static methods, etc., but the number of instances is limited and you can no longer create instances.
Custom member variables
For example, for the above enumeration class ColorEnum, I don't understand the meaning of each instance. For example: enumerating the instance RED, how do I know its meaning is RED? Well, I can add a description to it. It means RED. In this way, others will understand at a glance.
So what should we do?
You can add a parameter desc to the constructor to describe the meaning of enumerated instances. It needs to be passed in when declaring enumerated instances. Namely:
public enum ColorEnum { RED("gules") , GREEN("green") , BLUE("blue") ; private String desc; ColorEnum(String desc) { this.desc = desc; } }
be careful:
- There is only one constructor with parameters in this enumeration class, so you need to pass in a parameter when declaring an enumeration instance. You can also declare multiple construction methods (e.g. parameterless construction)
- If you intend to define attributes and methods in an enumeration class, you must use them after declaring the enumeration instance; separate. If any attribute or method is defined before enumerating instances, the compiler will report an error and cannot compile
- Even if the constructor is customized, we can never manually call the constructor to create enumeration instances. After all, it can only be done by the compiler
Define another parameterless construction method:
public enum ColorEnum { // Use parameterless construction WHITE , RED("gules") , GREEN("green") , BLUE("blue") ; private String desc; // Nonparametric structure ColorEnum() { } ColorEnum(String desc) { this.desc = desc; } }
After defining a parameterless constructor, you can declare a parameterless enumeration instance.
3. Member methods in enumeration
For the above enumeration class ColorEnum, we can easily know the meaning represented by each enumeration instance. However, is there any way to get the meaning represented by this instance?
Certainly. Think about how to get the properties of the object in the class? It is through the getter() method of the attribute.
Custom member method
Let's define a getter() method just like a class. For example:
public enum ColorEnum { WHITE , RED("gules") , GREEN("green") , BLUE("blue") ; private String desc; ColorEnum() { } ColorEnum(String desc) { this.desc = desc; } public String getDesc() { return desc; } }
Next, get it in the code:
public static void main(String[] args) { ColorEnum red = ColorEnum.RED; // gules System.out.println(red.getDesc()); }
From the results, this method is also applicable in enumeration classes.
Think about it: since the enumeration class has a method getter() to get the value, does it have a method setter() to set the value?
4. The setter() method of enumerating the properties of the class
Let's modify the above code:
public enum ColorEnum { RED("gules") , GREEN("green") , BLUE("blue") ; private String desc; ColorEnum(String desc) { this.desc = desc; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } }
In the above code, the setter() method is added to the original code.
Next, we call in the main() method:
public static void main(String[] args) { ColorEnum red = ColorEnum.RED; // gules System.out.println(red.getDesc()); red.setDesc("black"); // black System.out.println(red.getDesc()); }
Hey, hey, the setter() method also works.
[note]: Although the setter() method in the enumeration class can change the value, which is syntactically reasonable, it should not be used in the project.
So why?
In two ways:
- The meaning of the enumeration class itself
- Possible problems caused by using setter() method in enumeration class
The meaning of the enumeration class itself
Enumeration is just a syntax sugar, which will eventually be generated by the compiler, and the enumeration instance will become a static constant. Therefore, in a sense, jdk1 The enumeration type introduced in 5 is the code encapsulation of enumeration constant class. When the setter() method is used to modify the value, it is actually the value of a static variable in memory, and the original meaning of this value is modified
Possible problems caused by using setter() method in enumeration class
What problems may this cause? (there will be no problem with the setter() method itself)
You can see the following scenarios:
There is an order enumeration class, which encapsulates the status of the order. In normal business logic, corresponding processing will be made according to the order status.
The code is as follows:
Order class order:
public class Order { // Primary key private String sId; // Order status private Integer orderStatus; // Order time private Date orderTime; // getter/setter/toString ... }
Order status enumeration class OrderStatusEnum:
public enum OrderStatusEnum { WAIT_PAID(1001, "Pending payment") , HAS_PAID(1002, "Paid") , DELIEVER(1003, "Delivery in progress") ; // Status code private Integer code; // status information private String msg; OrderStatusEnum(Integer code, String msg) { this.code = code; this.msg = msg; } // Get OrderStatusEnum according to code public static OrderStatusEnum getOrderEnumByCode(Integer code) { OrderStatusEnum[] values = OrderStatusEnum.values(); for (OrderStatusEnum value : values) { if (value.getCode().equals(code)) { return value; } } return null; } // getter/setter/toString() }
Order business logic class:
public class OrderServiceImpl { // place an order public Order saveOrder(Order order) { Order o = new Order(); o.setOrderTime(new Date()); if (null == o.getsId()) { o.setsId(UUID.randomUUID().toString()); } // After placing the order, the order status is to be paid o.setOrderStatus(OrderStatusEnum.WAIT_PAID.getCode()); return o; } // payment public Order payMoney(Order order) { // Normally, query through orderId Order o = new Order(); o.setsId(order.getsId()); // Only when the status is to be paid, the status will be updated to paid if (OrderStatusEnum.WAIT_PAID.getCode().equals(order.getOrderStatus())) { o.setOrderStatus(OrderStatusEnum.HAS_PAID.getCode()); } o.setOrderTime(order.getOrderTime()); return o; } // Delivery public Order deliever(Order order) { // Normally, query through orderId Order o = new Order(); o.setsId(order.getsId()); // Only when the status is to be paid, the status will be updated to paid if (OrderStatusEnum.HAS_PAID.getCode().equals(order.getOrderStatus())) { o.setOrderStatus(OrderStatusEnum.DELIEVER.getCode()); } o.setOrderTime(order.getOrderTime()); return o; } }
be careful:
- In payMoney() and deliever() methods, when modifying the order status, first judge the order status in advance. The corresponding business logic processing will be carried out only if the order status meets the requirements. For example, only when the order status is "to be paid", call payMoney() method to modify the order status successfully; If the deleever () method is called, the order status modification fails
Test:
public class OrderEnumTest { public static void main(String[] args) { OrderServiceImpl orderService = new OrderServiceImpl(); // Just placed an order Order order = orderService.saveOrder(new Order()); // After payment Order payMoney = orderService.payMoney(order); System.out.println(payMoney); System.out.println("Order status:" + OrderStatusEnum.getOrderEnumByCode(payMoney.getOrderStatus()).getMsg()); // Delivery in progress Order deliever = orderService.deliever(payMoney); System.out.println(deliever); } }
Under normal circumstances, when orderservice is successfully called After the deliever (paymoney) method, your order status is "being dispatched".
However, the order status is encapsulated in the enumeration class, and the code value of the order status can be modified in the enumeration class. Therefore, if the status code value of the order is modified in the business logic somewhere. Namely:
// A business logic modifies the code value of the order status OrderStatusEnum orderStatusEnum = OrderStatusEnum.getOrderEnumByCode(payMoney.getOrderStatus()); orderStatusEnum.setCode(1001);
The above operation will the enumeration instance has in the enumeration class OrderStatusEnum_ Modify the code value of pad to 1001.
The complete code is as follows:
public class OrderEnumTest { public static void main(String[] args) { OrderServiceImpl orderService = new OrderServiceImpl(); // Just placed an order Order order = orderService.saveOrder(new Order()); // After payment Order payMoney = orderService.payMoney(order); System.out.println(payMoney); System.out.println("Order status:" + OrderStatusEnum.getOrderEnumByCode(payMoney.getOrderStatus()).getMsg()); // A business logic modifies the code value of the order status // The enumeration instance at this time is HAS_PAID OrderStatusEnum orderStatusEnum = OrderStatusEnum.getOrderEnumByCode(payMoney.getOrderStatus()); orderStatusEnum.setCode(1001); System.out.println(payMoney); // Null pointer exception reported here System.out.println("Order status:" + OrderStatusEnum.getOrderEnumByCode(payMoney.getOrderStatus()).getMsg()); ... } }
Why does the above code report null pointer exception?
analysis:
- After payment, the status of the order is: Order{sId='03ea74be-5509-4ec1-96e1-f60411b669a6', orderStatus=1002, orderTime =...}. Its orderStaus is 1002.
- Then the code value of the order status is modified. orderStatusEnum.setCode(1001). What is the impact of this operation? It will enumerate instances of HAS_PAID (OrderStatusEnum orderStatusEnum = OrderStatusEnum.getOrderEnumByCode(payMoney.getOrderStatus());) The code value in is changed from 1002 to 1001. Level: there is no enumeration instance with code value of 1002 in the enumeration class orderstatusenum
- After calling the method OrderStatusEnum When getmoney (getorder. Bymoney()), the The value of getorderstatus() is 1002. Therefore, the corresponding enumeration instance cannot be obtained in the enumeration class OrderStatusEnum. Therefore, null will be returned. When the getMsg() method is called, NPE is bound to occur.
The root causes of NPE are:
You have modified the code value of the enumeration instance in the enumeration class OrderStatusEnum:
orderStatusEnum.setCode(1001)
Then, in the code, you judge according to the code, and then obtain the corresponding enumeration instance:
public static OrderStatusEnum getOrderEnumByCode(Integer code) { OrderStatusEnum[] values = OrderStatusEnum.values(); for (OrderStatusEnum value : values) { // Judge according to code value if (value.getCode().equals(code)) { return value; } } return null; }
Eventually, the code is bound to go wrong.
The above scenario is only for the problems that may be caused by the setter() method in the enumeration class. Therefore, try not to use the setter() method of enumerating instance members in the class in development. Because you don't know what kind of problems may arise!!