Java Optional API Evolution
Join the DZone community and get the full member experience.Join For Free
In my previous article, you can see that I am quite fond of Optional class. Probably many of you agree with me that it is very useful and provides a lot of options. As of now, it consists of approx. 500 lines of code and 20 methods with a public access level (including hashCode, equals, toString, and Javadocs) with various possibilities.
Today, I will show you what this API evolution looks like through the subsequent Java releases and what were the reasons for its introduction to Java. I hope that it will give you some more knowledge about Optional class itself, help to convince your coworkers to start using it instead of a simple null check or even to decide which Java version to use in your company or private project.
Let’s start with a simple question.
Why Does Optional Even Exist in Java Today?
Here, the reason is pretty simple. In many modern-day programming languages there is a feature (or a bug, depending on what you think) called “the billion-dollar mistake” or a null pointer reference if you prefer. In the case of Java, it allows
NullPointerException to occur.
The NPE is one of the most common if not the most common runtime exception. It tends to appear in the most unexpected and unwanted places of our codebase causing a great deal of trouble to us and our customers. Until Java 8, the only way to deal with them was to use “if ladder”, responsible for checking if the object is not null, what could make our code look like the following sample.
As you can see this code is fairly complex and long. It has to make each non-null check explicitly and in a separate statement that is why it ended up with a lot of boilerplate. This issue was one of the main reasons behind the introduction of Optional to Java. To show you the basic features of Optional I wrote a piece of code with the same meaning as the one above, using Java 8 version of the class.
As you can see here, almost everything is done for us. First, we wrap our task with Optional. Then, we execute a sequence of map operations on it. Finally, if our Optional is empty, or we weren't able to get value at some point, we throw an exception with the help of
I leave it up to you to decide which one is more readable and meaningful for you but for me the answer is pretty obvious.
The introduction is done.
It is time for a short description of what we get in the first version of Optional API.
In the previous paragraph, I presented some basic features of Optional. Other useful methods include:
of(T value), which is similar to
ofNullable(T value)but should only be used when you want to create optional from non-null value.
flatMap(Function<? super T,Optional<U>> mapper)
filter(Predicate<? super T> predicate)
If value is present in our Optional instance, both of them work in the same way as their counterparts in Stream interface.
ifPresent(Consumer<? super T> consumer)
It takes Consumer interface and invoke it when value in Optional is present otherwise it does nothing.
orElseThrow(Supplier<? extends X> exceptionSupplier)
It takes Supplier interface and if the value is not present it throws an exception created by a supplier.
I purposefully did not describe all methods from Java 8 Optional because this article focuses mainly on what was added in subsequent releases, not on what exactly it looked like at the beginning. If you wish to get more familiar with all the methods check the link to the official Oracle documentation:
It is the first stop of our journey.
This version brings the highest number of new methods to our API, namely:
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
Here the definition is pretty simple, if the value is present it will apply a Consumer action to it, otherwise it performs a supplied runnable action.
or(Supplier<? extends Optional<? extends T>> supplier)
If the value is not present this method will return Optional provided by supplier function.
Here definition is also pretty obvious. If our value is present it converts our Optional into Stream containing that value. If not, we get empty Stream. Such a conversion gives us access to all methods declared in Stream API which is more powerful and provides more functionalities than the Optional API.
Unfortunately, in this version, Oracle decided to add only one new method to Optional class. On the one hand, it seems to be quite useful but on the other — not so much. I will describe it and leave it to you to rate how useful it can be.
Here, the exception which will be thrown is defined for us so we do not have to specify it. If we decide to invoke this method on empty, Optional we will get
The latest Java LTS version is our next stop.
Here, again we get only one new method. Which, in my opinion, is not very useful.
It has the following signature isEmpty() and works opposite to isPresent() method which exists since Java 8. To be precise, it returns True if value is not present in our Optional instance.
Java 12 and Above
Unfortunately, there were no updates in Optional API since Java 11. We will have to wait for Java 16 version release content plan for any new changes here because, as far as I know, there are no further changes planned for this API in Java 15. If such changes appear, this article will be updated.
I provided a brief description of how one of the most popular Java class change through time to achieve its current state. Through 6 major Java releases, we got 6 new methods which, as of now, are approx. 25% of all methods in Optional class. With them, we got new features and possible use cases. I hope that this article gave you a better understanding of how Optional API was changing with subsequent Java releases and why it could be better to use Java 9 or 11 over Java 8.
Opinions expressed by DZone contributors are their own.