iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…