Java Optional: Ten Methods, Five Real Runs

java.util.Optional<T> is a container that either holds a value or doesn’t. This post runs five small Java files, each compiled and run for real, covering ten Optional methods: creation and presence checks, unwrapping, conditional actions, transformation/filtering, and streams/equality.

Creating an Optional, and checking what’s inside

Optional.of wraps a non-null value, Optional.empty() makes an empty one, and Optional.ofNullable picks between the two depending on whether its argument is null. isPresent/isEmpty report which state you’ve got.

import java.util.Optional;

public class CreatingCheckingDemo {

    public static void main(String[] args) {
        Optional<String> present = Optional.of("hello");
        Optional<String> empty = Optional.empty();
        Optional<String> fromNullable = Optional.ofNullable(null);
        System.out.println("Optional.of(\"hello\") = " + present);
        System.out.println("Optional.empty() = " + empty);
        System.out.println("Optional.ofNullable(null) = " + fromNullable);

        System.out.println("present.isPresent() = " + present.isPresent());
        System.out.println("present.isEmpty() = " + present.isEmpty());
        System.out.println("empty.isPresent() = " + empty.isPresent());
        System.out.println("empty.isEmpty() = " + empty.isEmpty());
    }
}

Real javac+java output, JDK 25 (Zulu):

Optional.of("hello") = Optional[hello]
Optional.empty() = Optional.empty
Optional.ofNullable(null) = Optional.empty
present.isPresent() = true
present.isEmpty() = false
empty.isPresent() = false
empty.isEmpty() = true

Unwrapping: get, orElseThrow, orElse, orElseGet

get() and orElseThrow(supplier) both throw when the Optional is empty; orElse(value) and orElseGet(supplier) both fall back to a default instead of throwing when it’s empty.

import java.util.Optional;

public class UnwrappingDemo {

    public static void main(String[] args) {
        Optional<String> present = Optional.of("hello");
        Optional<String> empty = Optional.empty();

        System.out.println("present.get() = " + present.get());
        try {
            empty.get();
        } catch (Exception e) {
            System.out.println("empty.get() threw " + e.getClass().getName());
        }
        try {
            empty.orElseThrow(() -> new IllegalStateException("no value"));
        } catch (Exception e) {
            System.out.println("empty.orElseThrow(...) threw " + e.getClass().getName() + ": " + e.getMessage());
        }

        System.out.println("empty.orElse(\"default\") = " + empty.orElse("default"));
        System.out.println("empty.orElseGet(() -> \"computed\") = " + empty.orElseGet(() -> "computed"));

        int[] callCount = {0};
        String result = present.orElseGet(() -> {
            callCount[0]++;
            return "computed";
        });
        System.out.println("present.orElseGet(...) = " + result + ", supplier called " + callCount[0] + " times");
    }
}

Real javac+java output, JDK 25 (Zulu):

present.get() = hello
empty.get() threw java.util.NoSuchElementException
empty.orElseThrow(...) threw java.lang.IllegalStateException: no value
empty.orElse("default") = default
empty.orElseGet(() -> "computed") = computed
present.orElseGet(...) = hello, supplier called 0 times

Acting on a value without unwrapping it: ifPresent, ifPresentOrElse, or

ifPresent(consumer) runs the consumer only when a value is there; ifPresentOrElse(consumer, emptyAction) adds a second callback for the empty case; or(supplier) returns another Optional as a fallback instead of a raw value.

import java.util.Optional;

public class ActingDemo {

    public static void main(String[] args) {
        Optional<String> present = Optional.of("hello");
        Optional<String> empty = Optional.empty();

        present.ifPresent(v -> System.out.println("present.ifPresent ran with: " + v));
        empty.ifPresent(v -> System.out.println("empty.ifPresent ran with: " + v));
        present.ifPresentOrElse(
                v -> System.out.println("present.ifPresentOrElse: value branch, v=" + v),
                () -> System.out.println("present.ifPresentOrElse: empty branch"));
        empty.ifPresentOrElse(
                v -> System.out.println("empty.ifPresentOrElse: value branch, v=" + v),
                () -> System.out.println("empty.ifPresentOrElse: empty branch"));

        Optional<String> fallback = empty.or(() -> Optional.of("fallback"));
        System.out.println("empty.or(() -> Optional.of(\"fallback\")) = " + fallback);
        Optional<String> unchanged = present.or(() -> Optional.of("fallback"));
        System.out.println("present.or(() -> Optional.of(\"fallback\")) = " + unchanged);
    }
}

Real javac+java output, JDK 25 (Zulu):

present.ifPresent ran with: hello
present.ifPresentOrElse: value branch, v=hello
empty.ifPresentOrElse: empty branch
empty.or(() -> Optional.of("fallback")) = Optional[fallback]
present.or(() -> Optional.of("fallback")) = Optional[hello]

Transforming and filtering: map, flatMap, filter

map(fn) applies fn to the wrapped value and rewraps the result, passing an empty Optional through unchanged; flatMap does the same but for a function that itself returns an Optional, avoiding a nested Optional<Optional<T>>; filter(predicate) keeps the value only if the predicate matches.

import java.util.Optional;

public class TransformFilterDemo {

    public static void main(String[] args) {
        Optional<String> present = Optional.of("hello");
        Optional<Integer> length = present.map(String::length);
        System.out.println("present.map(String::length) = " + length);

        Optional<String> empty = Optional.empty();
        Optional<Integer> emptyLength = empty.map(String::length);
        System.out.println("empty.map(String::length) = " + emptyLength);

        Optional<Optional<String>> nested = Optional.of(Optional.of("nested"));
        Optional<String> flat = nested.flatMap(o -> o);
        System.out.println("nested.flatMap(o -> o) = " + flat);

        Optional<String> tooShort = present.filter(v -> v.length() > 10);
        Optional<String> longEnough = present.filter(v -> v.length() > 2);
        System.out.println("present.filter(len > 10) = " + tooShort);
        System.out.println("present.filter(len > 2) = " + longEnough);
    }
}

Real javac+java output, JDK 25 (Zulu):

present.map(String::length) = Optional[5]
empty.map(String::length) = Optional.empty
nested.flatMap(o -> o) = Optional[nested]
present.filter(len > 10) = Optional.empty
present.filter(len > 2) = Optional[hello]

Streams and equality: Optional::stream, equals

Optional::stream turns an Optional into a 0- or 1-element Stream, so flatMap(Optional::stream) over a list of optionals drops the empties; equals compares the wrapped values, and two empty Optionals are equal to each other.

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

public class StreamsEqualityDemo {

    public static void main(String[] args) {
        List<Optional<String>> optionals = List.of(
                Optional.of("a"), Optional.empty(), Optional.of("b"), Optional.empty(), Optional.of("c"));
        List<String> collected = optionals.stream()
                .flatMap(Optional::stream)
                .toList();
        System.out.println("flatMap(Optional::stream) over " + optionals.size() + " optionals = " + collected);

        Optional<String> a = Optional.of("hello");
        Optional<String> b = Optional.of("hello");
        Optional<String> c = Optional.of("world");
        Optional<String> e1 = Optional.empty();
        Optional<String> e2 = Optional.empty();
        System.out.println("Optional.of(\"hello\").equals(Optional.of(\"hello\")) = " + a.equals(b));
        System.out.println("Optional.of(\"hello\").equals(Optional.of(\"world\")) = " + a.equals(c));
        System.out.println("Optional.empty().equals(Optional.empty()) = " + e1.equals(e2));
    }
}

Real javac+java output, JDK 25 (Zulu):

flatMap(Optional::stream) over 5 optionals = [a, b, c]
Optional.of("hello").equals(Optional.of("hello")) = true
Optional.of("hello").equals(Optional.of("world")) = false
Optional.empty().equals(Optional.empty()) = true

Takeaway

Across the five runs: of/ofNullable/empty wrap a value, null, or nothing; get()/orElseThrow throw on empty while orElse/orElseGet fall back; ifPresent/ifPresentOrElse ran their callbacks conditionally and or supplied a fallback Optional only when empty; map/flatMap/filter transformed or passed through the wrapped value; and Optional::stream plus equals behaved as their printed output shows — three empties dropped out of five optionals, two empty Optionals equal to each other.