Working with Optional API

1. Creating Optional

FactoryBehavior
Optional.empty()Empty singleton
Optional.of(v)Throws NPE if null
Optional.ofNullable(v)Empty if null
OptionalInt/Long/DoublePrimitive specializations

2. Checking Presence

MethodReturnsUse
isPresent()booleanAvoid — prefer functional API
isEmpty() Java 11+booleanInverse of isPresent
ifPresent(c)voidSide effect when present

3. Retrieving Values

MethodBehavior
get()Throws NoSuchElementException if empty
orElse(default)Always evaluates default
orElseGet(supplier)Lazy default
orElseThrow() Java 10+Throws NoSuchElementException
orElseThrow(supplier)Custom exception
Warning: Prefer orElseGet over orElse when default is expensive (e.g., DB lookup) — orElse always evaluates its argument.

4. Throwing Exceptions (orElseThrow)

FormUse
orElseThrow()Default NoSuchElementException
orElseThrow(NotFoundException::new)Domain exception
orElseThrow(() -> new X("id=" + id))Contextual message

5. Transforming Values

MethodSignatureBehavior
map(Function)Optional<U>1:1 transform; empty stays empty
flatMap(Function)Optional<U>Function returns Optional
filter(Predicate)Optional<T>Empty if predicate false

Example: Chained transform

String city = Optional.ofNullable(user)
    .map(User::getAddress)
    .map(Address::getCity)
    .orElse("Unknown");

6. Filtering Optional (filter)

Inputfilter(p) Result
emptyempty
present, p truesame Optional
present, p falseempty

7. Chaining Optional Operations

PatternResult
map.map.mapNested transforms; empty propagates
flatMapAvoids Optional<Optional<T>>
filter.mapConditional transform

8. Using ifPresent (Consumer)

MethodAction
ifPresent(c)Run consumer if value
ifPresentOrElse(c, runnable)Both branches Java 9+

9. Using ifPresentOrElse (Java 9+)

Example: Branching

repository.findById(id).ifPresentOrElse(
    user -> log.info("Found {}", user),
    () -> log.warn("Missing id={}", id)
);
ParamTypeWhen
actionConsumer<? super T>Value present
emptyActionRunnableEmpty

10. Using or() for Alternative Optional (Java 9+)

MethodReturns
or(Supplier<Optional>)This if present, else supplier's Optional
orElseRaw value
orElseGetRaw value, lazy

Example: Cascading lookups

User u = cache.find(id)
    .or(() -> database.find(id))
    .or(() -> remote.find(id))
    .orElseThrow(() -> new NotFoundException(id));

11. Avoiding Common Pitfalls

Anti-patternFix
Optional fields/paramsOnly return type — never field, param, or collection element
opt.get() without checkUse orElseThrow or functional ops
isPresent + getUse ifPresent / map
Optional.of(possiblyNull)Use ofNullable
Serializing OptionalNot Serializable — use null in DTOs
orElse(new Heavy())Use orElseGet

12. Using Optional with Streams (stream() method)

MethodUse
opt.stream() Java 9+0 or 1 element stream
flatMap(Optional::stream)Filter + unwrap pattern

Example: Filter present values

List<User> found = ids.stream()
    .map(repo::findById)        // Stream<Optional<User>>
    .flatMap(Optional::stream)  // Stream<User> (drop empties)
    .toList();