How to use and how not to use Optional in Java

Krzysztof Mazur

If you are bored by null checking objects and want to make your code more readable, you should consider using Optional class. It is a container used to represent null with absent value.

Introduction

If you are bored by null checking objects and want to make your code more readable, you should consider using Optional class. It is a container used to represent null with absent value.

Old null-check

if(house != null) {
    doSomething(house);
}

Optional newbie null-check

Optional<house> maybeHouse = Optional.ofNullable(house);
if(maybeHouse.isPresent()) {
    doSomething(maybeHouse.get());
}

How to use Optional correctly

It is almost the same as „old” null-check and it is an anti-pattern.

  • ofNullable(T value) Returns an Optional describing the specified value, if non-null, otherwise returns an empty Optional.
  • isPresent() Return true if there is a value present, otherwise false.
  • get() If a value is present in this Optional, returns the value, otherwise throws NoSuchElementException.

And the proper way

Optional<house> maybeHouse = Optional.ofNullable(house);
maybeHouse.ifPresent(this::doSomething);

You can use method reference or lambda to make code more readable

  • isPresent(Consumer; consumer) If a value is present, invoke the specified consumer with the value, otherwise do nothing.

Default value

The Optional class provides APIs for returning the value of the object or a default value if the object is empty.

Table table1 = null;
Table table2 = new Table("green");
Table result = Optional.ofNullable(table1).orElse(table2);

So if first object is null, then return second object instead. If first object isn’t null so default value will be ignored

  • orElse(T other) Return the value if present, otherwise return ther.

You can also throw exception if object is null

Table table1 = null;
Table result = Optional.ofNullable(table1).orElseThrow(IllegalStateException::new);
  • orElseThrow(Supplier exceptionSupplier) Return the contained value, if present, otherwise throw an exception to be created by the provided supplier.

Filter

Usually you have to check property of your nullable object and then came filter method

For example you want to know if table1 is black

Table table1 = new Table("green");
Boolean isBlueTable = Optional.ofNullable(table1)
        .filter(e -> e.getColour() == "black")
        .isPresent();
  • filter(Predicate predicate) If a value is present, and the value matches the given predicate, return an Optional describing the value, otherwise return an empty Optional.

FlatMap

Now let’s take a look at the previous java version cascading problem

String tableColour = house.getRoom().getTable().getColour();

To take a colour of a table which is in the room in your house, you have to check if there is not any null on your way to prevent a NullPointerException

String tableColour;
if(house != null) {
    Room room = house.getRoom();
    if(room != null) {
        Table table = room.getTable();
        if(table != null) {
             tableColour = table.getColour();
        }
    }
}

Now your code is safe but due to the nested null checks it is not clean and looks ugly. Optional gives you alternative and clean way to handle multiple null checks

First of all update your class to make use of Optional

class House {
    Optional<Room> room;
    public Optional<Room> getRoom() {
        return room;
    }
}

class Room {
    Optional<Table> table;
    public Optional<Table> getTable() {
        return table;
    }
}

class Table {
    String colour;
    public String getColour() {
        return colour;
    }

...

After refactoring you don’t need to use nested null check and it is more clear that for example Room can have a table, but it is not necessary.

Let take a look at the new way of null checking using flatmap and map

String tableColour = Optional.ofNullable(house)
        .flatMap(House::getRoom)
        .flatMap(Room::getTable)
        .map(Table::getColour)
        .orElse("black");

Code is clean and more readable

  • map(Function< super T,? extends U> mapper) If a value is present, apply the provided mapping function to it, and if the result is non-null, return an Optional describing the result.
  • flatMap(Function> mapper) If a value is present, apply the provided Optional-bearing mapping function to it, return that result, otherwise return an empty Optional.Conclusion

To sum up Optional is one of the most useful features from Java 8+ which will help you everywhere you have to stand against NullPointerException

Bibliography:

https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami