Imagine that there is a cat, a radioactive substance and a flask with hydrocyanic acid in the box. Substances are so small that only one atom can decay within an hour. If it disintegrates within an hour, the reader will be discharged, a relay will operate, which will activate the hammer, which will break the flask, and the Karachun will come to the cat. Since the atom may decay, or it may not, we do not know whether the cat is alive or not, therefore, it is both alive and dead at the same time. This is a thought experiment called the Schrödinger's Cat.

The Optional class has similar properties — when writing code, the developer often cannot know whether the necessary object will exist at the time the program is executed or not, and in such cases it is necessary to do null checks. If such checks are neglected, then sooner or later (usually sooner) your program will crash with a NullPointerException.
')
Colleagues! The article, like any other, is not perfect and can be corrected. If you see the possibility of a significant improvement in this material, please indicate it in the comments.How to get an object through Optional?
As already mentioned, the Optional class can contain an object, or it can contain null. For example, we will try to extract from the repository the user with the specified ID:
User = repository.findById(userId);
Perhaps the user of such an ID is in the repository, and perhaps not. If there is no such user, a NullPointerException arrives in our glass frame. If we didn’t have a reserve of class Optional, we would have to invent some such construction:
User user; if (Objects.nonNull(user = repository.findById(userId))) { ( ) }
Agree not so much. Much nicer to deal with this line:
Optional<User> user = Optional.of(repository.findById(userId));
We get an object in which the requested object can be - or maybe null. But with Optional it is necessary to work somehow further, we need the entity that it contains (or does not contain).
There are only three categories of Optional:
- Optional.of - returns an Optional object.
- Optional.ofNullable returns an 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 follow from knowing if a wrapped object exists or not - isPresent () and ifPresent ();
.ifPresent ()
The method allows you to perform some action if the object is not empty.
Optional.of(repository.findById(userId)).ifPresent(createLog());
If we usually perform some kind of action in the case when the object is absent (more on this later), here it is just the opposite.
.isPresent ()
This method returns the answer, whether the required object exists or not, in the form of a Boolean:
Boolean present = repository.findById(userId).isPresent();
If you decide to use the get () method described below, then it will not be superfluous to check if the object exists, using this method, for example:
Optional<User> optionalUser = repository.findById(userId); User user = optionalUser.isPresent() ? optionalUser.get() : new User();
But this design personally seems cumbersome to me, and we will discuss the more convenient methods for obtaining an object below.
How to get the object contained in Optional?
There are three direct methods for further obtaining an object of the orElse () family; As follows from the translation, these methods work if there is no object in the received Optional.
- orElse () - returns an object by default.
- orElseGet () - calls the specified method.
- orElseThrow () - throws an exception.
.orElse ()
Suitable for cases where we definitely need to get an object, even if it is empty. The code, in this case, might look like this:
User user = repository.findById(userId).orElse(new User());
This construction is guaranteed to return us an object of class User. She really helps out at the initial stages of cognition Optional, and also, in many cases, associated with the use of Spring Data JPA (where most of the classes of the find family return it is Optional).
.orElseThrow ()
Very often, and again, in the case of using Spring Data JPA, we need to explicitly state that there is no such object, for example, when it comes to an entity in the repository. In this case, we can get the object or, if not, throw an exception:
User user = repository.findById(userId).orElseThrow(() -> new NoEntityException(userId));
If the entity is not detected and the object is null, a NoEntityException exception will be thrown (in my case, a custom one). In my case, the client leaves the line “User {userID} not found. Check the request details. "
.orElseGet ()
If the object is not found, Optional leaves space for “Option B” - you can perform another method, for example:
User user = repository.findById(userId).orElseGet(() -> findInAnotherPlace(userId));
If the object was not found, it is proposed to search elsewhere.
This method, like orElseThrow (), uses Supplier. Also, through this method you can, again, call the default object, as in .orElse ():
User user = repository.findById(userId).orElseGet(() -> new User());
In addition to methods for obtaining objects, there is a rich toolkit for converting an object, morally inherited from stream ().
Work with the received object.
As I wrote above, Optional has a good toolkit for transforming the resulting object, namely:
- get () - returns an object, if any.
- map () - converts an object to another object.
- filter () - filters the contained objects by predicate.
- flatmap () - returns a set in the form of a stream.
.get ()
The get () method returns an object packed in Optional. For example:
User user = repository.findById(userId).get();
A User object packed in Optional will be received. Such a construction is extremely dangerous, since it bypasses null checking and makes sense of the use of the Optional itself, since you can get the desired object, or you can get NPE. Such a construction would have to be wrapped in .isPresent ().
.map ()
This method completely repeats the similar method for stream (), but only works if Optional has a non-null object.
String name = repository.findById(userId).map(user -> user.getName()).orElseThrow(() -> new Exception());
In the example, we got one of the fields of the User class, packaged in Optional.
.filter ()
This method is also borrowed from stream () and filters elements by condition.
List<User> users = repository.findAll().filter(user -> user.age >= 18).orElseThrow(() -> new Exception());
.flatMap ()
This method does exactly the same as Stream's, with the only difference that it only works if the value is not null.
Conclusion
The Optional class, when skillfully used, greatly reduces the ability of the application to crash with a NullPoinerException, making it more comprehensible 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, since the same Spring drives it in its methods and into the tail and into the mane. However, Optional is an acquisition of Java 8, which means that knowing it in 2018 is simply a must.