Optional
Optional<T> wraps a value that might be missing. Forces you to ackowledge null at compile time; with map / orElse / ifPresent, you write code without NullPointerExceptions.
Create, transform, unwrap, anti-patterns
EXAMPLE
import java.util.Optional;
import java.util.stream.Stream;
// 1) Create
Optional<String> some = Optional.of("hello");
Optional<String> emp = Optional.empty();
Optional<String> maybe = Optional.ofNullable(possiblyNull);
// 2) Check + unwrap
if (maybe.isPresent()) {
String s = maybe.get();
}
// More idiomatic:
maybe.ifPresent(s -> System.out.println(s));
maybe.ifPresentOrElse(
s -> System.out.println(s),
() -> System.out.println("empty")
);
// 3) Default with orElse / orElseGet / orElseThrow
String s1 = maybe.orElse("default");
String s2 = maybe.orElseGet(() -> expensiveDefault()); // lazy default
String s3 = maybe.orElseThrow(() -> new IllegalStateException("missing"));
String s4 = maybe.orElseThrow(); // NoSuchElementException
// 4) Transform — map
Optional<Integer> len = Optional.of("hello").map(String::length); // Optional[5]
Optional<Integer> none = Optional.<String>empty().map(String::length); // empty
// 5) Chain — flatMap (for functions returning Optional)
Optional<User> user = findUser(id);
Optional<String> email = user.flatMap(User::getEmail);
// vs user.map(User::getEmail) which would give Optional<Optional<String>>
// 6) Filter — keep only if predicate holds
Optional<Integer> positive = Optional.of(-3).filter(n -> n > 0); // empty
// 7) Convert to a Stream (Java 9+)
List<String> names = users.stream()
.map(User::getEmail)
.flatMap(Optional::stream)
.toList();
// flatMap(Optional::stream) drops empties + unwraps in one step
// 8) Or — fallback Optional
Optional<String> result = primary.or(() -> fallback);
// 9) Real-world patterns
// Find user by id, return their plan name (or 'free' if no plan)
public String planFor(long userId) {
return findUser(userId)
.flatMap(User::getSubscription)
.map(Subscription::getPlanName)
.orElse("free");
}
// Build something only if all parts are present
Optional<Address> address = street.flatMap(s ->
city.flatMap(c ->
postcode.map(p -> new Address(s, c, p))
));
// 10) Anti-patterns to AVOID
// 10a) Optional.get() without isPresent — same risk as null
String bad = maybe.get(); // throws if empty
// 10b) Optional as a method parameter — don't
// void process(Optional<User> user) { ... } // BAD
// void process(User user) { ... } // GOOD — caller decides
// 10c) Optional as a class field — generally don't
// class Order { Optional<Discount> discount; } // BAD
// class Order { Discount discount; } // null or a value; document it
// 10d) Optional.of(null) — throws NPE
// Use ofNullable when the value might be null.
// 10e) Optional<Collection> — return an empty Collection instead
// Optional<List<X>> ← awkward
// List<X> empty() ← idiomatic
// 11) Optional vs @@Nullable / @@NonNull
// In libraries / framework code: prefer @@Nullable annotations + IDE support.
// Optional is best for RETURN TYPES that signal "might be missing" semantically.
// 12) JDK 9+ niceties
Optional<String> a = Optional.of("x");
a.ifPresentOrElse(s -> log(s), () -> log("empty"));
a.or(() -> Optional.of("fallback"));
a.stream().forEach(System.out::println);
Why it matters
Use Optional as a RETURN TYPE to signal “might be missing.” Don’t use it for fields, parameters, or collections — the cost is noise; the benefit is gone.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
Optional<String> name = Optional.ofNullable(maybeName);
String out = name.map(String::toUpperCase).orElse("ANON");
Try it Yourself »
Discussion
Loading…