Optional

null has been called the billion-dollar mistake: any reference might secretly be absent, and forgetting to check causes NullPointerExceptions. Optional is a container that makes "there might be no value" explicit in a method's return type, and offers safe ways to deal with the empty case.

Creating Optionals

JAVA
import java.util.Optional;

public class Main {
    public static void main(String[] args) {
        Optional<String> some = Optional.of("Ana");
        Optional<String> none = Optional.empty();
        String maybeNull = null;
        Optional<String> fromNullable = Optional.ofNullable(maybeNull);

        System.out.println(some);
        System.out.println(none);
        System.out.println(some.isPresent() + " " + none.isEmpty() + " " + fromNullable.isEmpty());
    }
}
Output
Optional[Ana]
Optional.empty
true true true

Returning Optional from a method

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

record Customer(int id, String name, String email) {}

public class Main {
    static final List<Customer> CUSTOMERS = List.of(new Customer(1, "Ana", "ana@example.com"), new Customer(2, "Julia", null));

    static Optional<Customer> findById(int id) {
        return CUSTOMERS.stream().filter(c -> c.id() == id).findFirst();
    }

    public static void main(String[] args) {
        System.out.println(findById(1).map(Customer::name).orElse("unknown"));
        System.out.println(findById(9).map(Customer::name).orElse("unknown"));
    }
}
Output
Ana
unknown

The return type Optional<Customer> tells every caller "this can come back empty" — they can't forget, because they must unwrap it.

Getting the value out

JAVA
import java.util.Optional;

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

        System.out.println(city.orElse("Lisbon"));
        System.out.println(city.orElseGet(() -> "computed default"));
        city.ifPresentOrElse(c -> System.out.println("City: " + c), () -> System.out.println("No city"));
        try {
            city.orElseThrow(() -> new IllegalStateException("City required"));
        } catch (IllegalStateException e) {
            System.out.println(e.getMessage());
        }
    }
}
Output
Lisbon
computed default
No city
City required
MethodWhen empty
orElse(value)Returns the default (always evaluated)
orElseGet(supplier)Calls the supplier — use when the default is costly
orElseThrow()Throws NoSuchElementException (or your exception)
ifPresent(action), ifPresentOrElseRuns code only if a value is present
get()Throws — avoid; it defeats the purpose

Transforming: map, filter, flatMap

JAVA
import java.util.Optional;

record Customer(String name, String email) {}

public class Main {
    static Optional<String> domainOf(Optional<Customer> customer) {
        return customer
            .map(Customer::email)
            .filter(e -> e.contains("@"))
            .map(e -> e.substring(e.indexOf('@') + 1));
    }

    public static void main(String[] args) {
        System.out.println(domainOf(Optional.of(new Customer("Ana", "ana@example.com"))));
        System.out.println(domainOf(Optional.of(new Customer("Julia", null))));
        System.out.println(domainOf(Optional.empty()));
    }
}
Output
Optional[example.com]
Optional.empty
Optional.empty

map transforms the value if present; if the function returns null, the result becomes empty. A chain of operations stays safe without a single if (x != null).

Guidelines

  • Use Optional as a return type for "may not find a result" methods.
  • Don't use it for fields, method parameters or collection elements — that adds clutter without much benefit.
  • Never return null from a method declared to return Optional.
  • For collections, return an empty collection rather than Optional<List> or null.
  • OptionalInt, OptionalDouble and OptionalLong avoid boxing.

Try it yourself

Look up a product's discount in a map, defaulting to zero, and apply it.

Try it yourself · Main.java
Edit the code, copy it into Main.java and run java Main.java (Java 25+). How to set up Java
Output
mug: 10% off
throw: 0% off
lamp: 25% off