The ultimate guide to java.util.Optional class

April 2, 2020

by — Posted in Core Java

Hello! This post covers a topic of using java.util.Optional. This class provides a container that may or may not contain same value. In the article you can find theoretical aspects, approaches to create new Optional instance and methods.

Introducing Java Optional

The concept of optional is not something new and has been already implemented in functional programming languages like in Haskell or Scala. It proves to be very useful when modeling cases when a method call could return an unknown value or a value that does not exist (e.g. nulls). In Java, it is defined as a container object which may or may not contain a non-null value.

From a technical point of view, Optional is what is called value-based class. That means following:

  • Optional instance is immutable and final
  • Implementations of hashCode,equals and toString methods affect only instance state
  • Such instances are considered equal solely based on equals() method’s implementation, not based on reference equality (==)

Also, Optional class does not have constructors, and new instances are created with factory methods.

Create optional instance

Advice

Code examples in this post use AssertJ library to write fluent assertions. You can learn more on how to utilize AssertJ with Java Collections in this tutorial

There are several approaches to create a new instance of java.util.Optional object. As it was mentioned in the previous section, all approaches utilize factory methods instead of constructors.

Using of()

The most common way to initialize a new Optional instance is by using of() static factory method. It accepts a value and creates a new instance with this value. Keep in mind, that the value can’t be null, otherwise would be thrown NullPointerException.

Take a look on the code snippet below:

Optional<Integer> result1 = Optional.of(10);
assertThat(result1).isInstanceOf(Optional.class).isPresent().contains(10);

From streams

Advice

In this section I use Java streams. If you’re not familiar with them, feel free to check my guide on Java Streams API

Another way to obtain an Optional is using streams. Several terminal stream operations return java.util.Optional instances as a result, so we can manipulate them and checking existence or absence:

  • findAny
  • findFirst
  • max
  • min
  • reduce

Here is an example of using this approach:

List<Integer> numbers = Lists.newArrayList(1, 2, 3, 4, 5);
Optional<Integer> result2 = numbers.stream().filter(n -> n>2).findFirst();
assertThat(result2).isInstanceOf(Optional.class).isPresent().contains(3);

From nullable

The first mentioned method – of() has a disadvantage, that it requires the value to be non-null, otherwise it will throw NullPointerException. If value could be null it is always better to use safeguard method ofNullable. It creates a new instance with the specified value, otherwise it returns an empty optional.

String name = null;
Optional<String> result3 = Optional.ofNullable(name);
assertThat(result3).isInstanceOf(Optional.class).isEmpty();

Create an empty Optional

Finally, there is a way to create explicitly empty Optional using empty static method. Here is an example code:

Optional<String> result4 = Optional.empty();
assertThat(result4).isInstanceOf(Optional.class).isEmpty();

How to use java.util.Optional

As long as we obtained Optional, we can use it. One of most widespread case is using it in Spring repositories in finding one record by id, so we can build our logic on Optional and avoid null checking. java.util.Optional brings a number of useful methods, that we will observe in this section.

ifPresent()

This is the most common situation. When you need to execute some logic with the value, based on the fact that it is presented, you can use ifPresent() method. It invokes a consumer function with the value, or does nothing if optional is empty.

The code snippet below demonstrates a simple scenario, when a value is printed to the standard output:

@Test
void onPresentTest(){
    String name = "Diana";
    Optional<String> result = Optional.of(name);
    result.ifPresent(n -> System.out.println(n));
}

ifPresentOrElse()

The mentioned before method executes logic only if the optional instance is not empty. If you need to provide an alternative solution for absent value, it makes sence to use ifPresentOrElse(). Note, that this method is available since JDK 9.

It accepts two arguments in form of functions:

  • Consumer function of action, that is performed when the value is present
  • Runnable, which is executed when the value is absent

Take a look on the following example:

@Test
void onPresentOrElseTest(){
    Optional<Integer> result = Optional.empty();
    result.ifPresentOrElse(n -> System.out.println("Result is: " + n), 
                          () -> System.out.println("No result found"));
}

get() and orElse()

We already mentioned, that Optional object serves as a container that can hold a value. In order to obtain the value, we need to “unpack” the container. There are two ways to do so:

  • With get() which returns a value or throws NullPointerException if value is absent
  • Using orElse that can specify an alternative return value, if the container is absent

Here is a demonstration:

@Test
void onElseGetTest(){
    Optional<Integer> result = Optional.empty();
    Integer value = result.orElse(10);
    assertThat(value).isEqualTo(10);
}

orElseThrow()

This method, introduced in JDK 11 have two possible outcomes:

  • If value is presented – return it
  • If no value – throw NoSuchElementException

Take a look on the following code snippet:

@Test
void onElseThrowTest(){
    Optional<String> result = Optional.empty();
    assertThatExceptionThrownBy(() -> result.orElseThrow()).isInstanceOf(NoSuchElementException.class);
}

map()

Optional brings a mapping functionality with map() method. It applies mapper function to the value (if it is presented) and returns optional with modified value or returns empty optional if no value available. Here is an example source code:

@Test
void mapTest(){
    Optional<String> name = Optional.of("Diana");
    Optional<String> result = name.map(n -> n.toUpperCase());
    assertThat(result).isPresent().contains("DIANA");
}

Create stream from Optional

The final thing I would like to talk about in this post is how to create a stream from Optional. We already mentioned, that there is a way to obtain an Optional instance as a result of stream terminal operations. Alike, you can create a stream from the container’s value with stream() method.

For example, this code demonstrates how to get stream from Optional’s value and count elements (although, there is only 1 element here):

@Test
void streamTest(){
    Optional<Integer> number = Optional.of(10);
    long result = number.stream().count();
    assertThat(result).isEqualTo(1);
}

Source code

You can find the full source code for this post in this github repository. If you have questions regarding this post, don’t hesitate to contact me. Have a nice day!

References

  • José Paumard. Optionals: Patterns and Good Practices (2016) Oracle Community Directory, read here
  • Mervyn McCreight and Mehmet Emin Tok. A look at the Optional datatype in Java and some anti-patterns when using it (2019) FreeCodeCamp, read here