Java Optional Recipes

In this tutorial, we’ll walk through the Optional class methods that was introduced in Java 8 and improved with new features in the last versions of Java.

About

The optional class is a type of container for a value that may be absent.

Origin

NullPointerException, NullPointerException, NullPointerException… The code below will throw the NullPointerException at runtime if there’s no null checking before using the employee.

Employee employee = findEmployee("1234");
System.out.println("Employee's Name = " + employee.getName());

Also, there’s no difference between null of an absent value and null value resulted after a method call. There is no point to return null values if there is nothing to output.

In both cases, it should be a value type that represents the absence or not of a returned value. Therefore, in Java 8, a new type was added called Optional, which indicates the presence or absence of a value.

Optional has not been invented to get rid of the NullPointerException. But, its principal aim is to deal gracefully with null values in streams.

Optional is mainly used as a method return type when it is clearly necessary to indicate “no result” and the use of null may cause an error. Variables of type Optional must never be null. An Optional should always carry to an Optional instance.

Now, Oracle is advising to use Optional to get rid of the if statement.

When and how?

Create

There are diverse ways of creating Optional objects.

Do

The easiest is empty():

Optional<String> empty = Optional.empty();

We can likewise create an Optional object with the method of():

String name = "Java";
Optional<String> maybeName = Optional.of(name);

In case we expect any null value, we can use the method ofNullable():

String name = ...;
Optional<String> maybeName = Optional.ofNullable(name);

By using ofNullable(), a null reference will not cause an exception but it will return an empty Optional object.

Don’t

The argument passed to the method of() can’t be null. Unless we’ll get a NullPointerException:

String maybeName = Optional.of(null);

Note

Get The Value

The get() method is simply used to return a value from the Optional.

Don’t

Suppose the value is not present, then it throws the exception NoSuchElementException.

Optional<Employee> maybeEmployee = repository.getEmployeeById(1234);
Employee employee = maybeEmployee.get();

Do

So it is recommended to first check if the value is present or not before calling get().

Optional<Employee> maybeEmployee = repository.getEmployeeById(1234);
if (maybeEmployee.isPresent()) {
    Employee employee = maybeEmployee.get();
    ... // do something with "employee"
} else {
    ... // do something that doesn't call employee.get()
}

Note

Get The Value Or Else

The method orElse() is used to return a default value if the object is empty.

Don’t

Optional<String> maybeEmployeeStatus = repository.getEmployeeById(1234);
if (maybeEmployee.isPresent()) {
    Employee employee = maybeEmployee.get();
    ... // do something with "employee"
} else {
    ... // do something else like
    Employee employee = Employee.of("0", "Unknown");
}

Do

Optional<Employee> maybeEmployee = repository.getEmployeeById(1234);
Employee employee = maybeEmployee.orElse(new Employee("0", "Unknown"));

Note

Get The Value Or ElseGet

The method orElseGet() method accepts a Supplier and it is invoked when Optional is empty.

Don’t

When the employee will be returned from the cache, the database query is still called too. It is very expensive as an operation!

Optional<Employee> getFromCache(int id) {
    ...
}

Optional<Employee> getFromDB(int id) {
    ...
}

public Employee findEmployee(int id) {        
    return getFromCache(id).orElse(
        getFromDB(id).orElseThrow(
            () -> new NotFoundException("Employee not found with id" + id)
        )
    );
}

Do

By using orElseGet(), you will get a performance improvement:

public Employee findEmployee(int id) {        
    return getFromCache(id).orElseGet(
        () -> getFromDB(id).orElseThrow(
            () -> new NotFoundException("Employee not found with id" + id);    
        )
    );
}

Note

Get The Value Or Throw An Exception

There are situations when you need to throw an exception to show a value doesn’t exist.

Don’t

Employee maybeEmployee = repository.findById(id);
if (maybeEmployee.isPresent())
    return maybeEmployee.get();
else
    throw new NoSuchElementException();

Do

repository.findById(id).orElseThrow();

repository.findById(id)
    .orElseThrow(() -> new EmployeeException("Employee not found with id " + id));

Note

Get The Value If Present Only

Sometimes, we need to act only on the wrapped value when an Optional value is present.

Don’t

Optional<String> maybeName = Optional.of("Java");
if (maybeName.isPresent())
	System.out.println(maybeName.get().length());

Do

Optional<String> maybeName = Optional.of("Java");
maybeName.ifPresent(s -> System.out.println(s.length()))

Note

Get The Value For Empty-based Action

This method allows us to execute an action if the Optional is present or another action if not.

Don’t

Optional<Employee> employee = ... ;
if(employee.isPresent()) {
	log.debug("Found Employee: {}" , employee.get().getName());
} else {
	log.error("Employee not found");
}

Do

maybeEmployee.ifPresentOrElse(
    employee -> log.debug("Found Employee: {}",emp.getName()),
	() -> log.error("Employee not found")
);

Note

Return Optional

In some cases, if the Optional yields present, then return an Optional describing the value; otherwise, return an Optional provided by the supplying function.

Don’t

public Optional<String> getStatus(int id) {
    Optional<String> maybeStatus = ...
    if (maybeStatus.isPresent())
        return maybeStatus;
    else
        return Optional.of("Not started yet."); 
}

Don’t overuse the orElse() or orElseGet() methods to fulfill this type of operation because both methods return an unwrapped value.

public Optional<String> getStatus(int id) {
    Optional<String> maybeStatus = ...
    return maybeStatus.orElseGet(() -> Optional.<String>of("Not started yet."));
}

Do

public Optional<String> getStatus(int id) {
    Optional<String> maybeStatus = ...
    return foundStatus.or(() -> Optional.of("Not started yet."));
}

Note

Return Presence Status

In some cases, you need to get an Optional status regardless of whether it is empty.

Don’t

public boolean isEmployeeListEmpty(int id){
	Optional<EmployeeList> maybeEmployeeList = ... ;
	return !maybeEmployeeList.isPresent();
}

Do

You can directly use isEmpty() method, which returns true if the Optional is empty since Java 11.

public boolean isEmployeeListEmpty(int id){
	Optional<EmployeeList> maybeEmployeeList = ... ;
	return maybeEmployeeList.isEmpty();
}

Note

Filter

You can use filter on Optional objects.

Don’t

if(employee != null && employee.getGender().equalsIgnoreCase("MALE")) {    
    ...
    // do something
}

Do

maybeEmployee
    .filter(user -> employee.getGender().equalsIgnoreCase("MALE")) 
    .ifPresent(() -> {    
    	...
        // do something
    });

Note

Extract & Transform

You can use map to extract and transform Optional object values.

Don’t

if (employee != null) {
	Address address = employee.getAddress();
 	if (address != null && address.getCountry().equalsIgnoreCase("TN")) {
  		System.out.println("Employee belongs to TUNISIA");
 	}
}

Do

maybeEmployee
    .map(Employee::getAddress)
    .filter(address -> address.getCountry().equalsIgnoreCase("TN"))
    .ifPresent(() -> {
 		System.out.println("Employee belongs to TUNISIA");
	});

Note

Cascading Optional

Employee#getAddress is returning an Optional.

Don’t

Optional<Optional<Address>> maybeAddress = 
            maybeEmployee.map(Employee::getAddress);

Do

Optional<Address> addressOptional = 
               maybeEmployee.flatMap(Employee::getAddress)

Note

Conclusion

We have seen what Java Optional is, its benefits, and dilemmas. Use it. But use it wisely. Optional is meant to be used as a return type. Trying to use it as a field type is not recommended. Additionally, we were able to better learn various antipatterns of Optional by studying some illustrative examples.