Tuesday 16 July 2019

Toggleable Enum in Java

I came across this below code in my code base where we just want to toggle the enum value. If it is DEBIT return CREDIT, if CREDIT return DEBIT like below.

enum DebitCreditCode implements Toggleable<DebitCreditCode> {
   DEBIT,
   CREDIT
public static DebitCreditCode getReverseOfPaymentDebitCreditCode(DebitCreditCode
                                                                     debitOrCreditFromSummary) {
   if (debitOrCreditFromSummary != null && debitOrCreditFromSummary
                                                 .equals(DebitCreditCode.CREDIT)) {
      return DebitCreditCode.DEBIT;
   } else if (debitOrCreditFromSummary != null && debitOrCreditFromSummary
                                                        .equals(DebitCreditCode.DEBIT)) {
      return DebitCreditCode.CREDIT;
   }
   return null;
}

This method is excellent and works as expected.
 
But…
If we want such requirement for any other enum,
  • we have to define similar utility method for each enum and it is a repetitive job
  • we will be landed up with duplicate code
So, to get rid of this, I have designed the below solution through which we can achieve the same without writing new method every time.

Step – 1Define this below interface in your common module. This is a one time job.

interface Toggleable<T extends Enum<T>> {
   default T toggle() {
      final T[] enumConstants = (T[]) this.getClass().getEnumConstants();
      if (enumConstants == null) {
         throw new UnsupportedOperationException("Only enum can support toggling");
      }
      if (enumConstants.length != 2) {
         throw new UnsupportedOperationException("Enum must have 2 objects to support toggling");
      }
      return enumConstants[0] == this ? enumConstants[1] : enumConstants[0];
   }
}

Step – 2Just implement the above interface to your enum class where you want this 
toggle/reverse feature like below :

enum DebitCreditCode implements Toggleable<DebitCreditCode> {
   DEBIT,
   CREDIT
}

Step – 3 : Call method toggle() on the enum object like

final DebitCreditCode toggled = DebitCreditCode.DEBIT.toggle();
Assert.assertEquals(DebitCreditCode.CREDIT, toggled);
final DebitCreditCode original = toggled.toggle();
Assert.assertEquals(DebitCreditCode.DEBIT, original);

Find complete code here