Java Optional – No More NullPointerException In Your Code

When writing code, the developer often can not know whether the necessary object will exist at the program execution time or not, and in such cases it is necessary to do null checks. If you don’t do those checks, then sooner or later (usually early) your program will crash with NullPointerException (aka NPE).

Finally starting from Java 8 we have Java Optional class that makes a programmer life easier and I would say safer 🙂
Today we’ll see how to use this magic Optional in your code.

How to get an object through Optional?

Optional employee = Optional.of(repository.findById(employeeId));

Here we get an object that potentially might be null, or maybe it’s a real object. But we need somehow to work further with Optional, we need that entity inside of it (or we need to tell to Optional what to do in case when the object inside of it is null).

There are only three categories of Optional:

  • Optional.of – returns an Optional object
  • Optional.ofNullable – returns the Optional object, and if there is no generic object, returns an empty Optional object
  • Optional.empty – returns an empty Optional object.

There are also two methods that indicate is there present a wrapped object or not – isPresent() and ifPresent().

ifPresent() & isPresent()

Let the code speak for itself…

// ifPresent Example
Optional.of(repository.findById(employeeId)).ifPresent(doSomthing());

// isPresent Example
Boolean present = repository.findById(employeeId).isPresent();
// or this way
Optional<User> optionalEmployee = repository.findById(employeeId);
Employee e = optionalEmployee.isPresent() ? optionalEmployee.get() : new Employee();

How to get wrapped object from Optional

There are three direct methods of the orElse() family for obtaining the object inside Optional. These methods used in case when the object in the received Optional was not found.

  • orElse() – returns the object by default
  • orElseGet() – calls the specified method
  • orElseThrow() – throws an exception.

.orElse ()

Suitable for cases where we need to get an object, even if it’s empty. The code, in this case, might look like this:

Employee employee = repository.findById (employeeId).orElse(new Employee());

This design is guaranteed to return us an Employee object. It helps very much in the initial stages of learning Optional, and also, in many cases, related to the use of Spring Data JPA (where most classes of the find family return Optional).

.orElseThrow ()

Very often, in the case of using Spring Data JPA particulary, we need to explicitly state that there is no such object, for example, when it comes to the entity in the repository. In this case, we can get the object or, if it does not exist, throw an exception:

Employee employee = repository.findById(employeeId).orElseThrow(() -> new NoEntityException(employeeId));

If the entity is not found and the object is null, then NoEntityException exception will be thrown.

.orElseGet()

If the object is not found, Optional leaves space for “Option B” – you can execute another method, for example:

Employee employee = repository.findById(employeeId).orElseGet(() -> findInAnotherPlace(employeeId));

If the object was not found, it is suggested to look elsewhere.

This method, like orElseThrow(), uses the Supplier. Also, through this method, you can, again, call the default object, as in .orElse():

Employee employee = repository.findById(employeeId).orElseGet(() -> new Employee());

In addition to the methods for obtaining objects, there is a rich tool for transforming the object inherited from stream().

More useful methods

  • get() – returns an object, if any
  • map() – converts an object to another object
  • filter() – filters the contained objects by the predicate
  • flatmap() – returns a set in the form of a stream

.get()

The get() method returns an object packed in Optional. For example:

Employee employee = repository.findById(employeeId).get();

A Employee object, packed in Optional, will be received. Such a construction is extremely dangerous, because it passes the null test and deprives the sense of the use of Optional. Such a design must be wrapped with .isPresent().

.map()

This method repeats the same method for stream(), but only works if there is a non-null object in Optional.

String name = repository.findById (employeeId).map (employee -> employee.getName()).OrElseThrow(() -> new Exception());

In this example, we got one of the fields of the Employee class, packaged in Optional.

.filter()

This method is also borrowed from stream() and filters the elements by condition.

List <Employee> employees = repository.findAll(). Filter (employee -> employee.age> = 29).orElseThrow(() -> new Exception());

.flatMap()

This method does exactly the same as Stream, with the only difference that it works only if the value is not null.

Conclusions

The Optional class, with skillful use, greatly reduces the ability of an application to crash with a NullPoinerException, making it more understandable and compact than if you were doing countless null checks. And if you use popular frameworks, then you will have to study this class in depth, for example Java Spring drives it in its methods by default.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.