In this article we will be taking a look at switch statements and expressions on enums. If you are new to the switch expressions which was introduced in Java 14, take a look at my previous blog here.
Let us consider an Ecommerce application which maintains the status of an order. Let’s keep this simple and consider that the order can be in one of the following states – Placed,Confirmed,Cancelled,Delivered. This can be represented by using an enum as shown below:
public enum OrderStatus {
PLACED,
CONFIRMED,
CANCELLED,
DELIVERED
}
An order class contains this OrderStatus as shown below:
public class Order {
private OrderStatus orderStatus;
public void setOrderStatus(OrderStatus orderStatus) {
this.orderStatus = orderStatus;
}
public OrderStatus getOrderStatus() {
return orderStatus;
}
}
Switch statement and enum
Consider a processOrder method which makes use of a switch statement as shown below:
private void processOrder(OrderStatus orderStatus) {
String status = "";
switch (orderStatus) {
case PLACED, CONFIRMED, DELIVERED:
status = "Success";
break;
case CANCELLED:
status = "Fail";
break;
}
//Further processing with status.
}
This code will compile without any problem. There is however one problem with this code. If we were to add a new state, (let us call it RETURNED) to the OrderStatus enum but forget to handle this in all switch cases,the code will silently fail as this case is not handled. The usual practice is to add a default case above so that the code does not silently fail without any indication.With a default case, the code can be modified to throw an error indicating to us that there is a change in the enum which we need to take care of.
private void processOrder(OrderStatus orderStatus) {
String status = "";
switch (orderStatus) {
case PLACED, CONFIRMED, DELIVERED:
status = "Success";
break;
case CANCELLED:
status = "Fail";
break;
default:
throw new IllegalArgumentException("Invalid order status : " + orderStatus);
}
//Further processing with status.
}
Switch expressions and enum
If we refactor the processOrder method using switch expressions,the code would look like this:
private void processOrder(OrderStatus orderStatus) {
String status = switch (orderStatus) {
case PLACED, CONFIRMED, DELIVERED -> "Success";
case CANCELLED -> "Fail";
};
//Further processing with status.
}
If we were to introduce a new status RETURNED in the OrderStatus enum, the compiler immediately flags an error that the switch expression does not cover all the possible input values. We don’t need to write a default case that throws an Exception to handle this scenario.
private void processOrder(OrderStatus orderStatus) {
String status = switch (orderStatus) {
case PLACED, CONFIRMED, DELIVERED -> "Success";
case CANCELLED -> "Fail";
};
//Further processing with status.
}
So in case of switch expressions on enums, the case blocks must cover all possible values.
Switch expression and default block
In case of switch expressions on an enum, the compiler inserts a implicit default block if the code does not already have one.This is extremely useful since it would indicate a change in the enum defintion. The default block is inserted as shown below: ( snippet from the .class file)
private void processOrder(OrderStatus orderStatus) {
String var1;
switch(orderStatus) {
case PLACED:
case CONFIRMED:
case DELIVERED:
var1 = "Success";
break;
case CANCELLED:
case RETURNED:
var1 = "Fail";
break;
default:
throw new IncompatibleClassChangeError();
}
}
The default case is inserted if we don’t write one.
Conclusion
With the introduction of switch expressions in Java 14, code using enums in switch expressions is more robust. The compiler checks if all possible values have been handled and then inserts an implicit default case in case we don’t write one .
If the code containing the switch is not compiled and the enum is changed, the IncompatibleClassChangeError will signal an error. This feature will definitely avoid the silent failures that we have seen in switch statement using enums.