Optional is an object type that can either be empty or contains a non-null value. Introduced in versopn 8, it tried to soften the problems with NullPointerException by providing a new way to deal with the absence of a value. It is best to use it as a return value, whereby it makes code better readable, communicating to readers that some method might well return no value at all.

In the context of functional programming and streams, Optional is the return value of .max(), .min(), .findFirst(), .findAny() and .reduce(). For the latter, only the reduce method with one parameter returns an Optional. Optional fits well into streams, it has a .stream() method that returns a stream and .map(), .flatMap() and .filter() that return a new Optional.

Inquiring Optionals

Apart from the stream context, any Optional type value can be inquired to check whether it is empty or not and if not, what value it contains. These are the methods:

Method Description
get() If Optional is empty throws exception, otherwise returns value
ifPresent(Consumer c) Does nothing if empty, calls consumer with value if not empty
isPresent() Returns true or false
orElse(T other) Returns value or other parameter if empty
orElseGet(Supplier s) If empty returns result of supplier, otherwise returns value
orElseThrow() Throws NoSuchElementException if empty, otherwise returns value
orElseThrow(Supplier s) Throws exception created by calling supplier, otherwise returns value

Generally, 6 of the 7 methods return the value if the Optional is not empty. The only one that doesn’t is ifPresent() which calls a Consumer with value. If the Optional is empty, they all do something different:

  • Nothing (ifPresent(Consumer c))
  • Throw exception (get(), orElseThrow(), orElseThrow(Supplier s))
  • Return false (isPresent())
  • Return result of a Supplier (orElseGet(Supplier s))
  • Return the argument T of the method (orElse(T other))

In words, you have the option to return nothing, false, an alternative or an exception. The point of all this is that you can write code that deals with Optionals without have to write if-statements or ternary operations. It promotes readability.

Note: I realized that .get() and .orElseThrow() actually do exactly the same. Perplexity told me that it is better to use .orElseThrow() because it has a name that improves code readability. Optional.get() was part of the Java 8 introduction, .orElseThrow() came in version 10.

Creating Optionals

Other than Java methods returning Optionals, you can create them yourself as well. There are the three static methods available:

  • Optional.of(T value)
  • Optional.empty()
  • Optional.ofNullable(T value)

Optional.of(T value) can not be set with a null value, it will throw an exception then. Optional.empty() returns an empty Optional. Optional.ofNullable(T value) can be set with either a value or null, upon which an empty Optional is created.

Optionals in streams

You can use .stream() on an Optional. It creates a stream consisting of the value, or an empty stream if the Optional is empty. Furthermore there are the methods .map(), .flatMap() and .filter() that can be used on an Optional and they return a new Optional. The first two are part of the exam.

Optional.map(Function<? super T,? extends U> mapper) returns an Optional<U> containing the return value of the Function. This can be problematic if the function itself returns an Optional, you get an Optional within an optional then. This won’t compile if you expect just a regular Optional as return value.

To prevent this, use .flatMap(Function<? super T,? extends Optional<? extends U>> mapper) instead. FlatMap has Optional<u> as return type as well, but removes one wrapping layer so you don’t end up with a non-compilable nested Optional.

Addition: I looked at the implementation in the JDK, you can say the following:

  • Both map() and flatmap() propagate empty Optionals.
  • map() will wrap any return value of Function in an Optional, regardless what it is. If the return value is an optional, you get a nested optional. This nesting can go on forever in a long chain where Function outputs Optionals.
  • flatMap() ensures that only one layer of Optional will be returned. No nesting possible
  • If Function returns null, map() will return an empty Optional. As far as I understand, .flatMap() does too.
  • If you supply null as argument instead of Function, then you get a NullPointerException with both.

Optionals and primitives

The regular Optional object can only hold object types, not primitives. To work with Optionals and primitives there are some variants on the Option class, namely:

  • OptionalInt
  • OptionalDouble
  • OptionalLong

They have the same methods as the regular Optional, with some exceptions:

  • The .get() method of Optional becomes respectively .getAsInt(), .getAsDouble() and .getAsLong().
  • The parameter types of .orElseGet() are respectively IntSupplier, DoubleSupplier and LongSupplier (instead of Supplier).

One other point:

  • The return type of IntStream.average(), DoubleStream.average() and LongSTream.average() is OptionalDouble. Note that .average() doesn’t exist in the regular Stream class.

<
Previous Post
Functional programming
>
Next Post
Mutable result container