Definition
- Optional.of(value) wraps a non-null value. Throws NPE if null (bug detector).
- Optional.ofNullable(value) wraps a potentially null value. Null becomes empty().
- Optional.empty() represents explicit absence.
- orElse(default) returns the value or a default (always evaluated).
- orElseGet(Supplier) returns the value or a lazy default (evaluated only if empty).
- orElseThrow(Supplier) returns the value or throws a meaningful exception.
- map(Function) transforms if present, returns empty if absent.
- flatMap(Function) handles nested Optionals (when mapping returns Optional).
Before Optional, Rajesh's findAccount method at National Bank of India returned either an Account or null. Every caller had to remember if (account != null): forget once and you get NullPointerException at 2 AM. Optional is like the bank giving you a sealed envelope: there might be a statement inside, or it might be empty. You must open the envelope properly: envelope.orElse(defaultStatement) or envelope.ifPresent(statement -> display(statement)). You cannot accidentally forget to check.
Understanding the Concepts
What is Optional?
Optional
How do you create an Optional?
package com.bank.service;
import java.math.BigDecimal;
import java.util.Optional;
public class OptionalCreation {
record Account(String id, String holder, BigDecimal balance) {}
public static void main(String[] args) {
// of: wraps non-null (throws NPE if null)
Optional<Account> present = Optional.of(
new Account("SAV2001", "Rajesh", new BigDecimal("125000")));
// ofNullable: wraps potentially null (null -> empty)
Account fromDb = null; // simulating not found
Optional<Account> maybe = Optional.ofNullable(fromDb);
// empty: explicit absence
Optional<Account> absent = Optional.empty();
System.out.println("Present: " + present.isPresent());
System.out.println("Maybe: " + maybe.isPresent());
System.out.println("Absent: " + absent.isPresent());
}
}
Present: true
Maybe: false
Absent: false
How do you consume an Optional safely?
package com.bank.service;
import java.math.BigDecimal;
import java.util.Optional;
public class OptionalConsumption {
record Account(String id, String holder, BigDecimal balance) {}
static Optional<Account> findAccount(String id) {
if ("SAV2001".equals(id)) {
return Optional.of(new Account("SAV2001", "Rajesh", new BigDecimal("125000")));
}
return Optional.empty();
}
public static void main(String[] args) {
// orElse: default value
String name = findAccount("SAV9999")
.map(Account::holder)
.orElse("Unknown Customer");
System.out.println("Name: " + name);
// orElseThrow: fail fast
try {
Account acc = findAccount("SAV9999")
.orElseThrow(() -> new RuntimeException("Account not found"));
} catch (RuntimeException e) {
System.out.println("Error: " + e.getMessage());
}
// ifPresent: action only if present
findAccount("SAV2001")
.ifPresent(a -> System.out.println("Found: " + a.holder()));
// map chain: null-safe transformation
BigDecimal balance = findAccount("SAV2001")
.map(Account::balance)
.orElse(BigDecimal.ZERO);
System.out.println("Balance: Rs." + balance);
}
}
Name: Unknown Customer
Error: Account not found
Found: Rajesh
Balance: Rs.125000
How is Optional different from null checks?
| Aspect | Null return | Optional return |
|--------|------------|----------------|
| Type signal | None: T could be null | Explicit: Optional
| Caller awareness | Must remember to check | Type forces handling |
| Chaining | Nested if-null checks | map().filter().orElse() |
| NPE risk | High (forget one check) | Low (type system guides) |
What are common mistakes?
// WRONG: .get() without checking (defeats the purpose!)
// Account a = findAccount("SAV9999").get(); // NoSuchElementException!
// WRONG: isPresent() + get() (glorified null check)
// if (opt.isPresent()) { opt.get().doSomething(); }
// CORRECT: use functional API
findAccount("SAV2001")
.map(Account::balance)
.ifPresent(b -> System.out.println("Balance: Rs." + b));
What should a senior engineer know?
Optional is designed for method return types only, not fields, parameters, or collections. It is not Serializable. The functional API (map, flatMap, filter, orElse) is the idiomatic way to use Optional. isPresent+get is an anti-pattern.
Visual Overview
Optional flow:
──────────────
findAccount("SAV2001")
│
Optional<Account> ← present
│
.map(Account::balance)
│
Optional<BigDecimal> ← present (Rs.125000)
│
.orElse(BigDecimal.ZERO)
│
Rs.125000 ← result
findAccount("SAV9999")
│
Optional<Account> ← empty
│
.map(Account::balance)
│
Optional<BigDecimal> ← empty (map skipped)
│
.orElse(BigDecimal.ZERO)
│
Rs.0 ← default
Key Takeaway
Optional is a container that explicitly represents the presence or absence of a value. It replaces the ambiguous null-return pattern where callers can forget to check for null. With Optional, the method signature communicates that the value might be absent, and the caller is forced by the type system to handle it.
The key methods: of wraps non-null (throws NPE on null), ofNullable wraps potentially null, map chains transformations safely, orElse provides defaults, orElseThrow fails fast, and ifPresent executes conditional actions.
Never call .get() without checking. It defeats the entire purpose of Optional.
Remember one sentence: Optional makes absence explicit in the type system so callers cannot accidentally forget to handle null.
---