streams
beginnerOptional
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 nothing2
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 this4
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 emptyorElseorElseGet
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 meanisPresent()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 avoids19
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 Stream20
The eager-evaluation bug, concretely21
// 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