Optional in Java 8

Overview

Optional is a class introduced in Java 8, In this article will explain the usages and requirements of the Optional class.

Problem

Imagine you have the following nested object structure for a person who is associated with a bank and a bank is having a branch.

public class Person {
private Bank bank;
public Bank getBank() { 
     return bank; 
}
}
public class Bank{
private Branch branch;
public Branch getBranch() { 
      return branch; 
}
}
public class Branch{
private String name;
public String getName() { 
    return name; 
 }
}

Now, Imagine you want to get the branch name in your code like below.

public String getBranchName(Person person) {
  return person.getBank().getBranch().getName();
}

This code seems good, but what happens if a person doesn't have any bank, In that case, this code will throw a NullpointerException at run-time and stop your program. But that’s not all. What if the person was null? What if the method getBranch() method returned null too?

How we deal with this problem before Java 8

Before Java 8 we do defensive checking using if statement.

public String getBranchName(Person person) {
   if(person !=null){
       if(person.getBank()!=null){
            if(person.getBank().getBranch() != null){
                 return person.getBank().getBranch().getName();   // assuming every branch will have a name in any case
             } 
          }
      }
   return "Unknown"
}

Or few developers write the above code like this

 public String getBranchName(Person person) {
        if (person == null) {
            return "Unknown"
        }
        Bank bank = person.getBank();
        if (bank == null) {
            return "Unknown";
        }
        Branch branch = bank.getBranch();
        if (branch == null) {
            return "Unknown";
        }
        return branch.getName();

    }

Solution

Java 8 introduced a class called java.util.Optional<T> which is used to wrap the object which might be null. The first step before working with Optional is to learn how to create optional objects.

Using Optional.empty method

It is an empty optional object.

Optional<Bank> optBank = Optional.empty();
Using Optional.of method
Optional<Bank> optBank = Optional.of(bank);

If the bank is null, a NullPointerException would be immediately thrown. 

Using Optional.ofNullable

Using Optional.ofNullable you can create an Optional object that may hold a null value.

Optional<Bank> optBank = Optional.ofNullable(bank); 

If the bank is null, the resulting Optional object would be empty.

Replacing the Person object using Optional.

public class Person {
private Bank bank;
public Optional<Bank> getBank() { 
     return Optional.ofNullable(bank); 
}
}
public class Bank{
private Branch branch;
public Optional<Branch> getBranch() { 
     return Optional.ofNullable(branch); 
}
}
public class Branch{
private String name;
public String getName() { 
    return name; 
 }
}

Finding the branch name using Optional

public String getBranchName(Optional<Person> person){
         return person.flatMap(Person:getBank).flatMap(Bank::getBranch).map(Branch::getName).orElse("Unknown");
}

why we use flatMap and not used map method - because Person class getBank and Bank class getBranch both method returns Optional. if we will use map method the code will not compile and the return type will be Optional<Optional<Bank>>. To convert Optional<Optional<Bank>> to Optional<Bank>  we use flatMap method.

In the above code if getBank or getBranch will return empty Optional if any of them is null.

You can notice orElse method in the above code, it allows you to provide a default value for when the optional doesn’t contain a value. orElse method will be executed if any of the methods in this invocation chain returns an empty optional.

There are many other useful methods in Optional class. Let's have quick look.

Method Usages
empty Returns an empty Optional instance
filter If the value is present and matches the given predicate, returns this Optional; otherwise returns the empty one
flatMap If a value is present, returns the Optional resulting from the application of the provided mapping function to it; otherwise returns the empty Optional
get Returns the value wrapped by this Optional if present; otherwise throws a NoSuchElementException
ifPresent If a value is present, invokes the specified consumer with the value; otherwise does nothing
isPresent Returns true if there is a value present; otherwise false
map If a value is present, applies the provided mapping function to it
of Returns an Optional wrapping the given value or throws a NullPointerException if this value is null
ofNullable Returns an Optional wrapping the given value or the empty Optional if this value is null
orElse Returns the value if present or the given default value otherwise
orElseGet Returns the value if present or the one provided by the given Supplier otherwise
orElseThrow Returns the value if present or throws the exception created by the given Supplier otherwise

Conclusion

Optional is helpful in avoiding the NullPointerException. Using Optional can help you design better APIs in which, just by reading the signature of a method, users can understand that which field can have a optional value.