Jan 23 2020

One misconception about Java’s Optional

Category: TechnicalIuliana @ 15:26

Yes, my dear readers, this is a technical post. It’s not going to rock your world, it’s not going to give you an insight into this language that you’ve never had before, it will just make you think twice before writing code. This being said, let’s dig in.

When java.util.Optional was introduced in JDK 8, Java developers rejoiced because they could finally avoid the most common and hated exception in the language the NullPointerException. Just between you and me, I think it’s  useful to be able to have nulls, but using them correctly does require a certain mastery of programming in Java.

Before reading my article, I would recomment reading this one first. In case you do not have the time to, here’s a summary.

java.util.Optional is awesome because:

  1. NullPointerException is prevented at runtime.
  2. Null value checking is not required in the application.
  3. Boilerplate code is not required.
  4. It’s easy to develop clean and neat APIs.

I would like to attract your attention to number three in that list: Boilerplate code is not required. I would have formulated it as Writing boilerplate code can be avoided. But, really … can we avoid writing boileplate code using Optional? It depends. Certainly not for the example in that entry.

Let’s start with the setup. We need an Employee class that has two fields: id and name, both String values.

// Employee.java
package com.ic.one;

public class Employee {
    private String name;
    private String id;

    //constructor, setters and getters
    // or just use Lombok, whatever floats your boat
}

We also need a main class, that should contain two methods, one returning an Employee, one returning an Optional that will be used in the code that will be written to check exactly how much boilerplate can be avoided.

//IfPresentOrElse.java 
package com.ic.one;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

public class IfPresentOrElse {
    public static void main(String[] args) {
        // here we'can call our special methods, to make sure the code actually runs as expected
    }

    public static Employee findEmployee(String id) {
        return id.equals("1234") ? new Employee("Gigi Pedala","1234"): null;
    }

    public static Optional<Employee> findOptional(String id) {
        return id.equals("1234") ? Optional.of(new Employee("Gigi Pedala","1234")): Optional.empty();
    }
}

Now that we have our methods to retrieve an Employee, or an optional, let’s do some comparisons.
Up until JDK 8, you could write something like this:

//IfPresentOrElse.java 
...
    // JDK 8 without Optional, version one
    public static void jdk8SimpleVersionOne (){
        Employee employee =   findEmployee("1234");
        if( employee != null) {
            System.out.println("Employee name is " + employee.getName());
        }
    }
...

The previous code is the most simple thing you can do when querying for an Employee, if an instance is returned, you print its name. Starting with JDK 8, the same code can make use of the Optional type to avoid returning a null value.

//IfPresentOrElse.java 
...
    // JDK 8 with Optional, version one
    public static void jdk8SimpleWithOptionalVersionOne (){
        Optional<Employee> optional = findOptional("1234");
        optional.ifPresent(employee -> System.out.println("Employee name is " + employee.getName()));
    }
...

It is said on this big bubble called the Java development world, that Optional spares you the pain of doing a null check. Yes, it’s true. But in regards to avoiding boileplate code, allow me to be the devil’s advocate here. Replacing an if statement with an ifPresent call and a Lambda expression, it’s just replacing an old-style boilerplate code with new-style boileplate code. The new boileplate code, it does look prettyier and smarter though, right?

Anyway, let’s complicate the previous example, and introduce a message to be written when there is no Employee with id 1234 found, in other words, let’s see what can we do with an if-else statement.

//IfPresentOrElse.java 
...
    // JDK 8 without Optional, version one
    public static void jdk8ComplexVersionOne (){
        Employee employee =   findEmployee("1234");
        if( employee != null) {
            System.out.println("Employee name is " + employee.getName());
        } else {
            System.out.println("No employee with id 1234" );
        }
    }
...

What will Optional do for this scenario? Well, as it turns out …

//IfPresentOrElse.java 
...
    // JDK 8 with Optional, version one
    public static void jdk8ComplexWithOptionalVersionOne (){
        Optional<Employee> optional = findOptional("1234");
        if(optional.isPresent()) {
            System.out.println("Employee name is " + optional.get().getName());
        } else {
            System.out.println("No employee with id 1234" );
        }
    }
...

… not much. Boilerplate is still there, and the ifPresent function is no longer an option unless we want to write a monstrosity like this:

//IfPresentOrElse.java 
...
    // JDK 8 with Optional, monstrosity
    public static void jdk8ComplexWithOptionalMonstrosity (){
        Optional<Employee> optional = findOptional("1234");
        optional.ifPresent(employee -> System.out.println("Employee name is " + employee.getName()));
        if(optional.isEmpty()) {
            System.out.println("No employee with id 1234" );
        }
    }
...

Let’s try to simplify the no-Optional example even more, and try to have a single System.out.println call, by updating a local variale named message depending on the situation.(Employee found or not – that is the situation :D ). The code can be written like this:

//IfPresentOrElse.java 
...
    // JDK 8 without Optional, version two
    public static void jdk8ComplexVersionTwo (){
        Employee employee =   findEmployee("1234");
        String message = "No employee with id 1234";
        if( employee != null) {
            message = "Employee name is " + employee.getName();
        }
        System.out.println(message);
    }
...

Optional will not help much with this scenario either.

//IfPresentOrElse.java 
...
    // JDK 8 with Optional, version two
    public static void jdk8ComplexWithOptionalVersionTwo (){
        Optional<Employee> optional = findOptional("1234");
        String message = "No employee with id 1234";
        if(optional.isPresent()) {
            message = "Employee name is " + optional.get().getName();
        }
        System.out.println(message);
    }
...

So, what is there to do? The only thing that makes things a little bit better here is the JDK9 ifPresentOrElse method, added to the Optional class. This method is quite smart, because it allows us to define an action that does not involve the value of Optional, for cases where there isn’t one.

//IfPresentOrElse.java 
...
    // JDK 9 with Optional, version one
    public static void jdk9ComplexWithOptionalVersionOne (){
        Optional<Employee> optional = findOptional("1234");
        optional.ifPresentOrElse(
                (employee) -> System.out.println("Employee name is " + employee.getName()),
                () -> System.out.println("No employee with id 1234" )
        );
    }
...

It does look better, but news flash, the boiler plate just moved now to the horizontal. So, what if we really want to have a single System.out.println call? Well… things become even more convoluted, because variables used in Lambda expressions should be final or effectively final, so we need to use an atomic reference for our message. And this leads to the previous example being written like this:

//IfPresentOrElse.java 
...
    // JDK 9 with Optional, version two
    public static void jdk9ComplexWithOptionalVersionTwo (){
        Optional<Employee> optional = findOptional("1234");
        AtomicReference<String> message = null;
        optional.ifPresentOrElse(
                (employee) -> message.set("Employee name is " + employee.getName()),
                () -> message.set("No employee with id 1234" )
        );
        System.out.println(message.get());
    }
...

Damn, there is no way to get rid of this boileplate code, is it?

The truth is, in programming there will always be statements and constructions that will need to be used more than others. These are fundamental parts that make up a program, you cannot avoid using them. And there is a limit to how much they can be reduced in size that is specific to that language. It’s like your daily commute. Sure, there are multiple routes and you can walk, or cycle, or drive, or take the train, but depending your situation and your other plans for the day, there is a way to commute that is more suitable than the others. And in the end there is a limited list of ways to commute, and you will end up using them over and over again, until you quit your job and move to Ibiza.

All of the examples above can be compiled and run with JDK 9-14. And they all do the same thing. I won’t touch the subject of performance, because an investigation into which statement is quicker than the other is overkill for me. There is not a best way to do it. It’s just a matter of preference and keeping the code readable.

I’m not particularly fond of any of those, and I decide the way I write my code depending on the situation.

And since we talked about the limit of reducing boilerplate code that is specific to the language, do you want to see how the same things can be done in Kotlin?

It was a rethorical question, I know you do. :D

//IfPresentOrElse.kt 
...
package com.ic.one

data class  Employee(val name: String, val id: String)

fun main() {
    // 2
    println("Employee name is ${findEmployee("1234")?.name}")

    // 3
    val  employee = findEmployee("1234")
    employee?.let { println("Employee name is ${it.name}") }

    // 4
    println (if ( employee != null ) "Employee name is ${employee.name}" else "No employee with id 1234")
}

// 1
fun findEmployee(id: String): Employee? =  if (id == "1234") Employee("Gigi Pedala","1234") else null
...

So, what happens in there? Well… let me explain each numbered section in the previous code snippet:

  1. The findEmployee function returns an Employee instance or null. The compiler knows that because the return type of the function which is Employee?. That question mark is not a mistake, is how we specify in Kotlin that a function can return a null value.
  2. That function can be called by a println function (the equivalent of the Java’s System.out.println) and here’s the fun part: the question mark can be used to test the returned value. That is why the question mark is called the safe call operator in Kotlin. If it is null, the name property won’t be accessed, instead null is returned, which in the case of line 2, will cause the message Employee name is null to be printed in the console, if no employee with id “1234” exists.
  3. NullPointerException avoided, but the behaviour is not what we actually want. Because we do not want to print anything in case there is no Employee in this case. Well, that can be achieved by using the safe call operator and the let inline function. Combining these two results in a function being called only for values that are not null.
  4. This section has the same effect as a if-else statement with a different message being printed for each scenario.

Side note: Yes, Kotlin supports placeholders too. You have no idea how much I hate writing System.out.println(“Employee name is ” + employee.getName()) or System.out.println(“Employee name is “.concat(employee.getName())).

I hope this proves my point. Boilerplate cannot be avoided, but it can be reduced within the limits of the language. And Kotlin does allow for a better job to be done than Java. And it doesn’t require an Optional type for it.

I know this is not an advanced technical entry, I know it seems to be no other conclusion than: do it as you feel more comfortable doing it.

But, I know for sure  somebody will find this entry useful. You are welcome, my darlings!

Stay safe, stay happy!

Tags: , , ,

Leave a Reply