Optional Wooes

Composition Java 8 has added the construct of Optional. Optionals have come to solve the NullPointerException (The Worst Mistake of Computer Science).

The optional adds the option for composition, so instead of this code:

String tikalProgrammerSkillLevel= "UNKNOWN";
if(tikal!= null){
  Programmer programmer = tikal.getProgrammer();
  if(programmer != null){
    Skill skill = programmer .getSkill();
    if(skill != null){
      tikalProgrammerSkillLevel= skill.getLevel();
    }
  }
}

This can now be changed to:

tikal
.flatMap(Programmer::getProgrammer)
.flatMap(Address::getSkill)
.orElse(“UNKNOWN”);

Negative Composition So the advantages are very obvious. But…

I now have some code that has multiple verifications, and each method with return an option of an error, for example:

public static Optional<VerificationError> checkOne(String value){
   System.out.println("checkOne");
   return (value.equalsIgnoreCase("1")) ? Optional.of(one) : Optional.empty();
}

public static Optional<VerificationError> checkTwo(String value){
   System.out.println("checkTwo");
   return (value.equalsIgnoreCase("2")) ? Optional.of(two) : Optional.empty();
}

public static Optional<VerificationError> checkThree(String value){
   System.out.println("checkThree");
   return (value.equalsIgnoreCase("3")) ? Optional.of(three) : Optional.empty();
}

What I would like to do is call all the validations and return the first validation that finds an error. The problem is that if the optional is empty I want to continue to the next one. The standard map function works the other way around, when there is a value it will run.

Bad code A bad example would be:

public static Optional<VerificationError> checkBad(String value){
   return Optional.ofNullable(
           checkOne(value)
                   .orElse(checkTwo(value)
                           .orElse(checkThree(value).orElse(null))));
}

As you can see we have two problems here. One the code is not scalable, so if we have tens of verifications we will not be able to read the code. The second problem Is that all verifications are run, even if the first one returns an error. The result is correct, but all functions are evaluated:

Optional<VerificationError> verificationError = checkBad("2");
System.out.println("checkBad: " + verificationError.get());

Result: checkOne checkTwo checkThree checkBad: two

Eager Composition An example for a better composition solution is:

public static Optional<VerificationError> checkEager(String value){
   return checkOne(value)
           .map(Optional::of).orElse(checkTwo(value))
           .map(Optional::of).orElse(checkThree(value));
}

As you can see here, the code is very composable, and you can easily add as many validations as needed. The solution here, is to wrap each result will an Optional and then call the else. But here are well all methods are called eagerly:

verificationError = checkEager("2");
System.out.println("checkEager: " + verificationError.get());

Result: checkOne checkTwo checkThree checkEager: two

Lazy Composition So how do we solve the code so that only the methods that are needed are run? Here the Optional class has another method that is orElseGet. This method gets a Supplier interface as a parameter. This way the method creation is called but the supplier is not.

public static Optional<VerificationError> checkProper(String value){
   return checkOne(value)
           .map(Optional::of).orElseGet(() -> checkTwo(value))
           .map(Optional::of).orElseGet(() -> checkThree(value));
}

Result: checkOne checkTwo checkNotLazy: two

As you can see checkThree was not called at all. For the full code example see: https://github.com/chaimt/TurelUtils/blob/master/src/main/java/com/turel/examples/OptionalWooes.java

Backend/Data Architect

Backend Group
Thank you for your interest!

We will contact you as soon as possible.

Send us a message

Oops, something went wrong
Please try again or contact us by email at info@tikalk.com