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 checkOne(String value){

System.out.println(“checkOne”);

return (value.equalsIgnoreCase(“1”)) ? Optional.of(one) : Optional.empty();

}

public static Optional checkTwo(String value){

System.out.println(“checkTwo”);

return (value.equalsIgnoreCase(“2”)) ? Optional.of(two) : Optional.empty();

}

public static Optional 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 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 = 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 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 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

Advertisements