Optional — when `null` is returned

Viraj Turakhia
Decaffeinating Java
2 min readMar 15, 2020

--

Java programmers tend to perform null checks when they are not always required. Performing null check on a method return value is one example. This tendency is a result of lack of support from Java compiler or Java runtime. That is, Java does not and cannot guarantee that a given reference is always a non-null value.

Java is trying to reduce the number of unnecessary null checks with the introduction of Optional. This article talks about different Optional APIs and how they can be used to handle the null values gracefully.

Using A Default Value

If a method returns null then use a fall back value.

Without Optional

Restaurant best = findTheBestRestaurant(myPreference);
if(best == null) {
best = this.fallbackRestaurant;
}
best.bookATable();

With Optional — using orElse

Optional.ofNullable(findTheBestRestaurant(myPreference))
.orElse(this.fallbackRestaurant)
.bookATable();

Throwing An Exception

In some cases, a null returned value is an exceptional case, and we want to bubble up the exception.

Without Optional

Restaurant best = findTheBestRestaurant(myPreference);
if(best == null) {
throw new NoDataFoundException("none found");
}
best.bookATable();

With Optional — using orElseThrow

Optional.ofNullable(findTheBestRestaurant(myPreference))
.orElseThrow(() -> new NoDataFoundException("none found"))
.bookATable();

Calling Another Method As Fallback

And finally, in some cases, we might want to call another method to get the second-best choice.

Without Optional

Restaurant best = findTheBestRestaurant(myPreference);
if(best == null) {
// do not worry about the preference
best = findTheBestRestaurant();
}
best.bookATable();

With Optional — using orElseGet

Optional.ofNullable(findTheBestRestaurant(myPreference))
.orElseGet(() -> findTheBestRestaurant())
.bookATable();

Why Is The Code With Optional Better?

  1. Optional makes code more readable because it avoids the if-else blocks. It becomes significantly better when you have null checks in the nested if-else block.
  2. Having fewer blocks means fewer exit points which means more maintainable code.
  3. The code feels more natural (once you are familiar with the APIs).
  4. Reading a value in Optional opens up a rich set of APIs to further manipulate the read value. For example:
Optional.ofNullable(findBestRestaurant())
.filter(r -> r.hasVeganOptions() && r.isOpen())
.map(r -> r.getFreeTable())
.ifPresent(table -> table.book());

As compared to

Restaurant r = findBestRestaurant();
if(r != null) {
if(r.hasVeganOptions() && r.isOpen()) {
Table table = r.getFreeTable();
if(table != null) {
table.book();
}
}
}

One Last Thing

It is important to note the difference between orElse() and orElseGet(). Either of them can be used in the following scenario but one of them is more efficient.

Option 1

Optional.ofNullable(findTheBestRestaurant(myPreference))
.orElse(findTheBestRestaurant())
.bookATable();

Option 2

Optional.ofNullable(findTheBestRestaurant(myPreference))
.orElseGet(() -> findTheBestRestaurant())
.bookATable();

Option 2 is more efficient because findBestRestaurant() is called lazily (that is, only if Optional is wrapping a null value) whereas in Option 1 findBestRestaurant() is always called whether the Optional is wrapping a null value or not.

--

--

Viraj Turakhia
Decaffeinating Java

A software engineer with 17 years of experience and still learning.