All topics
streams
beginner

Optional

Express "value or absence" in the type system instead of returning null — making missing-value handling explicit.

Optional<T> is a container that either holds a value or is explicitly empty, introduced to make the possibility of absence visible in the type system. A method returning Optional<User> tells the caller, right in the signature, that there might be no user — something a bare User return that could be null never communicates. The goal is to turn silent NullPointerExceptions into a handling decision the compiler nudges you to make.

A sealed box that might be empty — you must explicitly open it (orElse, orElseThrow) instead of assuming there's something inside.

Key Concepts

1
You rarely create one directly; you receive it from APIs like Stream.findFirst, Map-style lookups, or your own methods built with Optional.of, Optional.ofNullable, and Optional.empty. The value comes from composing rather than unwrapping: map transforms the contained value if present, filter drops it if a predicate fails, flatMap chains another Optional-returning call, and the terminal orElse, orElseGet, or orElseThrow provides a default or raises a meaningful exception. Written this way, user.map(User::getAddress).map(Address::getCity).orElse("Unknown") walks a chain of possibly-absent values without a single null check.
Stream.findFirstMapOptional.ofOptional.ofNullableOptional.empty
2
Optional is widely misused, and interviews probe whether you know its intended scope. It is designed as a return type, not as a field or method parameter — it adds an allocation and is not Serializable, so it makes a poor model member, and an Optional parameter just shifts the null problem. Calling get() without first checking isPresent() reintroduces exactly the exception Optional was meant to prevent, so prefer orElse/orElseThrow. And never return null from a method declared to return Optional — that is the worst of both worlds.
SerializableOptionalget()isPresent()orElse