How to use and how not to use Optional in Java
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