Optional
|
This section documents the current Java release line, with Java 25 LTS as the reference point (no specific patch version is pinned), as published at the Java developer portal, the Java Tutorials, and the Java SE API specification — which are the references these pages are written and verified against. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Optional<T> is a
container that holds either exactly one non-null value or nothing. Its purpose is narrow: it makes
"there might be no result" explicit in a method return type, so a caller cannot silently ignore the
empty case the way a possible null gets ignored. This page covers when to use it, its transforming
and unwrapping methods, the primitive variants, and the ways it is commonly misused. See
dev.java: Optional.
Why Optional
A method that may have no answer has several poor choices and one good one: return null (callers
forget to check and hit a
NullPointerException
later), throw (costly, and "not found" is not exceptional), return a sentinel such as -1 or an empty
string (ad hoc, easy to misread) — or return an Optional that the type system forces the caller to
open before use.
import java.util.Optional;
record User(long id, String name) { }
interface UserRepository {
Optional<User> findById(long id); // "maybe a user" is part of the contract
}
// the caller cannot reach the User without handling the empty case
String label = repo.findById(42)
.map(User::name)
.orElse("unknown");
Optional is a return-type tool for a single "maybe missing" result. It is not a general
replacement for every null, and specifically not for fields, method parameters, or collection
elements — see the anti-patterns at the end.
Creating and Querying
Three factories build an Optional, and two predicates test one.
import java.util.Map;
import java.util.Optional;
Map<String, String> env = Map.of("HOME", "/root");
Optional<String> a = Optional.of("hi"); // value must be non-null, else NPE
Optional<String> b = Optional.ofNullable(env.get("X")); // null becomes empty
Optional<String> c = Optional.empty(); // always empty
boolean present = a.isPresent(); // true
boolean empty = c.isEmpty(); // true (isEmpty added in Java 11)
Transform the contents without unwrapping:
map
applies a function when a value is present and stays empty otherwise;
flatMap
does the same when the function itself returns an Optional, so the result is not a nested
Optional<Optional<T>>; and
filter
turns a present value into empty when a predicate fails.
Optional<User> user = repo.findById(42);
Optional<String> upperName = user.map(u -> u.name().toUpperCase());
// lookupManager returns Optional<User>; flatMap keeps the result flat
Optional<User> manager = user.flatMap(u -> repo.findById(u.id() + 1));
Optional<User> onlyAda = user.filter(u -> u.name().equals("Ada"));
Unwrapping
Once the pipeline is built, extract a value or react to its absence. Prefer the methods that never
throw over
get().
Optional<User> found = repo.findById(42);
User u1 = found.orElse(GUEST); // constant fallback (always evaluated)
User u2 = found.orElseGet(() -> loadGuest()); // lazy fallback (only built when empty)
User u3 = found.orElseThrow(); // NoSuchElementException if empty
User u4 = found.orElseThrow(() -> new UserNotFoundException(42)); // custom exception
found.ifPresent(u -> System.out.println(u.name()));
found.ifPresentOrElse(
u -> System.out.println(u.name()),
() -> System.out.println("no user"));
Optional<User> either = found.or(() -> repo.findById(FALLBACK_ID)); // another Optional if empty
long n = found.stream().count(); // 0 or 1; splices into stream pipelines
Use
orElseGet
rather than
orElse
when the fallback is expensive: orElse’s argument is evaluated even when the value is present.
`stream()
turns an Optional into a zero-or-one-element
Stream,
which collapses a stream of Optional down to just the present values:
import java.util.List;
import java.util.Optional;
List<Long> ids = List.of(1L, 2L, 3L);
List<User> users = ids.stream()
.map(repo::findById) // Stream<Optional<User>>
.flatMap(Optional::stream) // Stream<User> -- empties dropped
.toList();
Primitive Variants and Anti-Patterns
OptionalInt,
OptionalLong,
and
OptionalDouble
avoid boxing when the value is a primitive. They are what the primitive streams return from min,
max, average, and findFirst, and they expose getAsInt / getAsLong / getAsDouble rather than
get.
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.stream.IntStream;
OptionalInt max = IntStream.of(3, 1, 4, 1, 5).max();
int highest = max.getAsInt(); // 5
int safe = IntStream.of().max().orElse(0); // 0 -- stream was empty
OptionalDouble avg = IntStream.rangeClosed(1, 10).average(); // OptionalDouble[5.5]
double mean = avg.orElse(Double.NaN);
Common misuses to avoid:
-
Optionalinstance fields. They add an allocation per object, are notSerializable, and complicate every constructor; use a plain nullable field, or model the two states as distinct subtypes. AnOptionalcomponent of arecordused purely as a data-transfer object is the one borderline case some teams permit. -
Optionalmethod parameters. This forces every caller to wrap an argument. Provide an overload, or accept a nullable parameter and document it. -
opt.get()with no guard.get()throwsNoSuchElementExceptionon an emptyOptional; writeorElseThrow()instead — same effect, clearer intent — or avoid unwrapping withmap/orElse. -
Optionalof a collection. Return an emptyList,Set, orMapinstead ofOptional<List<T>>, so callers deal with one empty case rather than two. -
isPresent()followed byget(). That is anullcheck written the long way; replace it withmap,ifPresent,ifPresentOrElse, ororElse.
See Also
-
Streams and Collectors —
findFirst/findAnyreturnOptional, andOptional.stream()feedsflatMap. -
Exceptions — when absence is ordinary, return
Optional; when it is truly exceptional, throw. -
Collections Framework — returning empty collections rather than
Optionalof a collection. -
Dates and Times — modelling a "maybe no date" result without
null.