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 anOptional
objectOptional.ofNullable
– returns theOptional
object, and if there is no generic object, returns an emptyOptional
objectOptional.empty
– returns an emptyOptional
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 defaultorElseGet()
– calls the specified methodorElseThrow()
– 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 anymap()
– converts an object to another objectfilter()
– filters the contained objects by the predicateflatmap()
– 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.