Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › Java › Commonly used classes

Optional

Why does the default-value code in orElse run even when a value is present?

An Optional is a container that expresses that a value may or may not be present. Depending on how you create it (of, ofNullable, empty) and how you get the value out (get, orElse, orElseGet, map), a missing value leads either to an exception or to a default value being used. orElse and orElseGet look as if they give the same result, but they differ in when the code that creates the default value runs.

Checked against real execution · All 24 combinations were run on JDK 21, and the output and the exceptions were checked against the results

Concepts in this lab

Optional
This container class expresses in the type that a value may or may not be present. When a method returns it in place of null, the caller is guided to handle the case where there is no value. It is used mainly as a method return type.
orElse and orElseGet
These are the two methods that set the default value to use in its place when an Optional is empty. orElse(value) takes the default value itself as its argument, and orElseGet(function) takes a function (a lambda expression) that produces the default value. Java evaluates the arguments before it calls a method, so if you write orElse(makeDefault()), makeDefault() runs even when the Optional contains a value. orElseGet runs the function that it was given only when the Optional is empty. So use orElseGet when creating the default value takes time or when other work, such as saving or logging, happens along with it.
NullPointerException
This exception is thrown when code tries to call a method or read a field through a reference that is null. null means that there is no object that the reference refers to, and an object that does not exist cannot be asked to do anything, so the exception is thrown. Whether a value is null depends on the values at run time, so the compiler cannot catch it in advance and it shows up only when the code runs (it is an unchecked exception, so handling is not forced either). This exception is also thrown when null is passed to a method that is written not to accept null (such as Optional.of or Objects.requireNonNull). In places where a value may be absent, you make this exception less likely by checking for null first or by using Optional to show in the type that the value may be absent.
Lazy evaluation
This approach delays a computation until its result is actually needed. The intermediate operations of a stream do not run as soon as they are called. They run only when the terminal operation is called, and each element passes through the pipeline one at a time. The stream can process only as much as it needs and then stop. Lazy fetching in JPA follows the same idea: associated data is queried when it is actually used.
Lambda expression
This is a syntax, introduced in Java 8, for writing a function with no name in a compact way. It has the form (parameters) -> body, and it is used as the implementation of a functional interface, which has only one abstract method. It lets you pass behavior (code) to another method as if it were a value.

Try these

Running with the default settings

  1. String name = "kim": name refers to the string "kim".
  2. Optional.ofNullable(name): name is not null, so this creates an Optional that contains the value "kim".
  3. System.out.println(opt): This prints one line, Optional[kim]. The value it contains is inside the square brackets.
  4. makeDefault(): Before orElse is called, the argument in the parentheses is evaluated first, because Java works out the values to pass before it calls a method. Whether or not opt has a value, makeDefault() runs, "makeDefault ran" is printed, and "guest" is created.
  5. opt.orElse("guest"): A value is present, so orElse returns "kim". The "guest" that was just created is not used and is discarded.
  6. System.out.println(result): This prints one line, kim.

Result of this run

Code

import java.util.Optional;

public class Main {
    static String makeDefault() {                // method that creates the default value
        System.out.println("makeDefault ran");
        return "guest";
    }

    public static void main(String[] args) {
        String name = "kim";
        Optional<String> opt = Optional.ofNullable(name);
        System.out.println(opt);

        String result = opt.orElse(makeDefault());
        System.out.println(result);
    }
}

Assumptions of this model

For the 24 combinations (Optional.empty() does not use name, so there are 20 distinct cases), the printed lines and the exceptions were checked against the results of running the same code on JDK 21 on this PC. Whether makeDefault() ran was confirmed by whether the line that the method prints appears in the output. That map does not run the function when the Optional is empty is behavior stated in the documentation of Optional, and in execution it was confirmed by "no name" being printed without an exception. The exception message "No value present" is as of JDK 21. The NullPointerException from Optional.of(null) has no message.

Previous lab: Dates and times · Next lab: Exceptions and finally

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these