Skip to content
Theory · Lambdas and the Stream API · lesson 11 of 12

Optional

Back to the end of the belt. Sometimes the collector needs not a crate of everything that made it, but one box: the first one that passed all the stations. But what if nothing made it to the end?

The belt could hand back null. You have already met null: it looks like an ordinary reference until a method is called on it, and then the program fails with NullPointerException.

So the belt hands over its find in a clear case. The case either holds a box or is empty, and you can see which at once. Before taking the box, you look into the case. In Java, such a case is called Optional:

JAVA
import java.util.List;
import java.util.Optional;

public class Main {
    public static void main(String[] args) {
        List<String> words = List.of("cat", "zebra", "ox");
        Optional<String> found =
            words.stream().filter(w -> w.length() > 4).findFirst();
        Optional<String> missing =
            words.stream().filter(w -> w.length() > 9).findFirst();
        System.out.println(found.orElse("none"));   // zebra
        System.out.println(missing.orElse("none")); // none
    }
}

Let's go through the program line by line:

  1. In the first stream, filter lets through only words longer than four letters, and zebra reaches the end.
  2. findFirst() is a terminal operation: it takes the first element that reached the end and puts it in an Optional<String> case.
  3. In the second stream there are no words longer than nine letters, and findFirst() returns an empty case.
  4. orElse("none") hands over what is in the case, or the fallback value "none" if the case is empty. So the program prints zebra and none.

A case instead of null

Optional is a container object that holds either one value or nothing. A method that may find nothing returns an Optional, and its type tells you right away that there may be no result. An ordinary reference gives no such warning.

If you print a whole case, println shows Optional[zebra] for a full one and Optional.empty for an empty one. Like findFirst, a stream's max and min return a case: an empty list has neither a longest nor a shortest word.

Interviewers often ask why Optional exists. The answer: so that a missing result shows in the type instead of hiding in null.

Getting the value out

A case has several ways to hand over what it holds, and each treats an empty case in its own way.

orElse

orElse(x) returns the value from the case, or the fallback value x for an empty one. There is always a result, which makes it the calmest way.

isPresent and get

isPresent() answers whether there is anything in the case, and get() takes the value out. Together they are written like this: the check first, then get().

ifPresent

ifPresent takes a Consumer and runs it only if there is a value. For an empty case it does nothing.

JAVA
if (found.isPresent()) {
    System.out.println(found.get().length()); // 5
}
found.ifPresent(w -> System.out.println("found " + w)); // found zebra
missing.ifPresent(w -> System.out.println("found " + w));

Line by line:

  1. found.isPresent() gives true, so get() is safe and returns "zebra", and the print gives 5.
  2. found.ifPresent(...) prints found zebra.
  3. missing.ifPresent(...) prints nothing: the case is empty.

This is where the case picture is inaccurate. You can put another box into a real case, but an Optional does not change: it holds forever what the method put into it.

Common mistake

get() was called without looking into the case:

JAVA
System.out.println(missing.get()); // ✗
TEXT
Exception in thread "main" java.util.NoSuchElementException: No value present

The program fails: the case is empty, and get() throws an exception. It is the same trouble as with null, only under another name: the case did not help because nobody looked into it. Use orElse or ifPresent, and call get() only after checking isPresent().

Key points

  • Optional is a case that holds either one value or nothing; findFirst returns exactly that.
  • orElse(x) hands over the value or the fallback x, and ifPresent runs an action only if there is a value.
  • get() on an empty Optional throws NoSuchElementException, so it is not called without an isPresent() check.
  • An empty result in an Optional shows in its type, while null hides until the first method call.