JavaOptional

Optional

Optional<T>, added in Java 8, is a container object that either holds a value or holds nothing. It exists to make the possibility of a "missing" result explicit and impossible to ignore, replacing the old convention of returning null and hoping every caller remembers to check for it.
Optional is for return types, not fields or parameters
Optional was designed specifically as a return type for methods that might not have a result. Using it as a field type or a method parameter type is widely considered bad practice — it adds an extra layer of indirection and boxing overhead for cases where null or simply not passing the argument works just as well, and it makes classes harder to serialize. Keep Optional at the boundary where a method reports back to its caller.
Creating and Checking an Optional

Creating Optionals

Java
import java.util.Optional;

Optional<String> present = Optional.of("hello");
Optional<String> absent = Optional.empty();
Optional<String> maybeNull = Optional.ofNullable(null); // safe even if the value is null

System.out.println(present.isPresent()); // true
System.out.println(absent.isEmpty());    // true (Java 11+)
true
true
Avoid calling get() directly
Optional.get() returns the wrapped value — but throws NoSuchElementException if the Optional is empty. Calling get() without first checking isPresent() reintroduces exactly the kind of unchecked failure Optional was meant to prevent. In almost every real case, one of the methods below is the better choice.
orElse(), orElseGet(), orElseThrow(), ifPresent()

Instead of unwrapping an Optional with a manual if-check, Optional's real value comes from methods that describe what to do in the absent case declaratively.

Handling absence without an explicit if-check

Java
import java.util.Optional;

public class OptionalDemo {
    static String computeDefaultName() {
        return "anonymous";
    }

    public static void main(String[] args) {
        Optional<String> username = Optional.empty();

        // orElse: a plain fallback value
        System.out.println(username.orElse("guest"));

        // orElseGet: a fallback computed lazily (only runs if empty)
        System.out.println(username.orElseGet(OptionalDemo::computeDefaultName));

        // ifPresent: run code only when a value exists
        Optional.of("Nadia").ifPresent(name -> System.out.println("Hello, " + name));

        // orElseThrow: fail loudly with a meaningful exception if absent
        try {
            username.orElseThrow(() -> new IllegalStateException("No user logged in"));
        } catch (IllegalStateException e) {
            System.out.println("Caught: " + e.getMessage());
        }
    }
}
guest
anonymous
Hello, Nadia
Caught: No user logged in
Note
orElse() always evaluates its argument, even when the Optional is present — so orElse(expensiveCall()) pays that cost every time. orElseGet() takes a supplier instead, which only runs when the Optional is actually empty. Prefer orElseGet() whenever the fallback is anything more than a cheap constant.
Chaining with map() and filter()
Optional supports a small functional pipeline of its own. map() transforms the value if present (and stays empty if not), while filter() turns a present value into an empty Optional if it fails a predicate. Chaining these lets you express a whole sequence of "only if present" steps without a single explicit null check.

Chaining map() and filter()

Java
import java.util.Optional;

Optional<String> email = Optional.of("  ADA@EXAMPLE.COM  ");

String normalized = email
        .map(String::trim)
        .map(String::toLowerCase)
        .filter(e -> e.contains("@"))
        .orElse("invalid-email");

System.out.println(normalized);

Optional<String> blank = Optional.of("not-an-email");
System.out.println(
    blank.filter(e -> e.contains("@")).orElse("invalid-email")
);
ada@example.com
invalid-email

Method

Behavior

isPresent()/isEmpty()

Check whether a value exists (isEmpty() since Java 11)

get()

Returns the value or throws — avoid calling directly

orElse(value)

Fallback value, always evaluated

orElseGet(supplier)

Fallback computed lazily, only if empty

orElseThrow(supplier)

Throws a custom exception if empty

ifPresent(consumer)

Runs code only when a value is present

map()/filter()

Transform or conditionally clear the value, staying inside Optional

Tip
A good mental model: Optional is a return type meaning "check before you use this", enforced by the compiler rather than by convention or documentation comments.
  • Optional<T> represents a result that may or may not be present

  • Use it as a return type — not for fields or method parameters

  • Avoid calling get() directly; prefer orElse()/orElseGet()/orElseThrow()/ifPresent()

  • map() and filter() let you build null-check-free transformation pipelines