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 box that either holds a value or is empty. It exists so a method can say "there might be no answer here" in its signature, where the compiler and the reader can both see it.

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

Key Concepts

1
    User findUser(String id)             // might return null -- nothing warns you
    Optional<User> findUser(String id)   // clearly might find nothing
2
It does not prevent NullPointerException. It makes the possibility visible.
NullPointerException
3
Use it the way it was designed.
    findUser(id)
        .map(User::getAddress)     // if present, get the address
        .map(Address::getCity)     // if present, get the city
        .orElse("unknown");        // otherwise this
4
No null checks, and any missing step simply produces an empty box that flows through.
5
orElse versus orElseGet is a real bug, not a style choice.
    return findUser(id).orElse(createDefaultUser());     // ALWAYS runs
    return findUser(id).orElseGet(() -> createDefaultUser());  // only if empty
orElseorElseGet
6
The argument to orElse is evaluated before the method is called — that is how Java works. So createDefaultUser() runs every single time, even when a user was found. If it hits a database, you are paying for it on every request.
orElsecreateDefaultUser()
7
map versus flatMap. Use map when your function returns a plain value. Use flatMap when it returns another Optional, or you get a box inside a box:
mapflatMapOptional
8
    opt.map(this::findManager)      // Optional<Optional<User>>
    opt.flatMap(this::findManager)  // Optional<User>
9
isPresent() followed by get() misses the point.
    if (opt.isPresent()) { use(opt.get()); }   // a null check with extra steps
    opt.ifPresent(this::use);                  // say what you mean
isPresent()get()
10
get() throws when empty, which is why Java 10 added orElseThrow() as a clearer name for the same behaviour.
get()orElseThrow()
11
Where it does not belong. Not as a field: it is not serialisable and adds an object for every value, and a field can simply be null. Not as a method parameter: callers then have to wrap their arguments, and an overload reads better. Not inside a collection: an empty list already means "nothing here".
12
Use OptionalInt, OptionalLong and OptionalDouble for numbers, so each value is not boxed into an object.
OptionalIntOptionalLongOptionalDouble
13
What the interviewer is probing.1. "Why does Optional exist?" Probing: the purpose. Stalls: "To avoid null pointer exceptions." Moves up: to put "there might be nothing" into the method signature; it does not prevent NPEs by itself.
Optional
14
2. "orElse or orElseGet?" Probing: evaluation timing. Stalls: "They are the same." Moves up: orElse's argument always runs; use orElseGet for anything expensive.
orElseorElseGet
15
3. "map or flatMap?" Probing: precision. Stalls: guessing. Moves up: flatMap when the function itself returns an Optional.
mapflatMapOptional
16
4. "Would you use it as a field?" Probing: scope. Stalls: "Yes, it is safer." Moves up: no — not serialisable, extra object per value, and a nullable field already says it.
17
5. "What is wrong with isPresent() and get()?" Probing: idiom. Stalls: "Nothing." Moves up: it is a null check with more typing; ifPresent, map and orElseThrow express the intent.
isPresent()get()ifPresentmaporElseThrow
18
  Method reference, with the trap each avoids
19
  isPresent() / get()     a null check in disguise; avoid in review
  orElse(v)               v is ALWAYS evaluated, even when present
  orElseGet(supplier)     supplier runs only when empty  <- prefer
  orElseThrow()           Java 10+, clearer than get()
  orElseThrow(supplier)   custom exception
  map(fn)                 fn returns a value    -> Optional<R>
  flatMap(fn)             fn returns Optional   -> Optional<R>
                          (map here would give Optional<Optional<R>>)
  filter(pred)            empty if the predicate fails
  ifPresent(consumer)     side effect when present
  ifPresentOrElse(c, r)   Java 9+, both branches
  stream()                Java 9+, 0-or-1 element, composes with
                          flatMap over a Stream
20
  The eager-evaluation bug, concretely
21
    // findDefault() runs on EVERY call, even when a user was found
    return findUser(id).orElse(findDefault());
22
    // findDefault() runs only when the Optional is empty
    return findUser(id).orElseGet(() -> findDefault());
23
  Where it belongs
    return type        yes -- this is the purpose
    field              no  -- not serialisable, extra header per value
    method parameter   no  -- forces callers to wrap; overload instead
    collection element no  -- an empty collection already says it