An enum is a type of class that can group constants in a single type. They are used when different constants can be referenced for a related reason (for example, a Status can be “DONE”, “REJECTED” or “PROCESSING”). Even though an Enum starts only as Constants, Java lets us use them in a smarter way, enabling us to use Object Oriented Patterns like State.
"Smart" Enums are Enums that implement functions defined as abstract in the same reach of the enum. An abstract function in an enum has to be implemented by all of the instances on the enum, much like abstract methods in a class when being inherited.
The use of enums is really common in Java to define things like tags or classify states for an object. Let’s use Payment as an example. A Payment that can be in three different states, “In Progress”, “Validated”, “Canceled”. This representation is enough for the first iteration of our implementation, letting us store the status on a DB, transferring it as a constant, etc. But let’s assume that as time goes on, we start to have a lot more requirements out of our status logic. If we had to define different behaviors for a Payment depending on the state, for example, a function that wants to generate a message depending on the state of the Payment. When we want to define this behavior, a simple solution would be to implement a switch statement as following.
Now, let’s assume that instead of just one implementation that changes by the payment status, there are multiple scenarios that change. We have to re-write several switch statements. If we then have to add a fourth type of PaymentStatus called “REJECTED”, then we have to search for all the switch that use PaymentStatus to add a fourth case.
To avoid this unreadable way to handle the State of the Payment, we can simplify the code if we let the Enums themselves handle the logic for each scenario that could require a switch.
For example if there’s a function that wants to implement a message that depends on the state, we can define a function “getMessage()” and let each enum handle the logic.
Whenever we want to obtain the message of a state, we only need to do:
This gives us three advantages.
And there are multiple useful implementations for Smart Enums beyond the listed. An example would be double-dispatching.
Smart enums are an easier way to implement State Patterns. Since the functions are like any other functions, this approach can be combined with other patterns like Visitor or Strategy if needed, giving us a lot of flexibility for the implementations.