Optional<T> (Java 8+) là container chứa một giá trị hoặc rỗng — type-system bắt caller nghĩ đến trường hợp không có giá trị, thay vì để null lẻn vào.
Optional<String> a = Optional.of("hi"); // không cho null
Optional<String> b = Optional.ofNullable(maybeNull); // null → empty
Optional<String> c = Optional.empty();
String name = repo.findByEmail(email)
.map(User::getName)
.filter(n -> !n.isBlank())
.orElse("Anonymous");Method chính: isPresent, isEmpty (Java 11+), ifPresent, map, flatMap, filter, orElse(T), orElseGet(Supplier) (lazy), orElseThrow().
Quy tắc:
- ✅ Return type: Optional<User> findById(...) — use case chính.
- ❌ Field/parameter: đừng làm field/parameter — tốn memory, không serialize đẹp.
- ❌ Trong collection: List<Optional<X>> — vô nghĩa, dùng list rỗng.
- ❌ .get() không check — chỉ đổi NPE thành NoSuchElementException.
Bẫy hay gặp:
- orElse(expensiveCompute()) luôn gọi expensiveCompute() — dùng orElseGet(() -> ...) để lazy.
- Optional.of(maybeNull) throw NPE — phải ofNullable.
Optional không phải để thay null mọi nơi — chỉ làm return value rõ ràng "có thể không có".
Optional<T> (Java 8+) is a container that holds one value or is empty — the type system forces the caller to consider the absent case instead of letting null slip through.
Optional<String> a = Optional.of("hi"); // does not accept null
Optional<String> b = Optional.ofNullable(maybeNull); // null → empty
Optional<String> c = Optional.empty();
String name = repo.findByEmail(email)
.map(User::getName)
.filter(n -> !n.isBlank())
.orElse("Anonymous");Key methods: isPresent, isEmpty (Java 11+), ifPresent, map, flatMap, filter, orElse(T), orElseGet(Supplier) (lazy), orElseThrow().
Rules:
- ✅ Return type: Optional<User> findById(...) — the primary use case.
- ❌ Field/parameter: do not use as a field/parameter — wastes memory, serialises awkwardly.
- ❌ In collections: List<Optional<X>> — meaningless, use an empty list.
- ❌ Unchecked .get() — just swaps NPE for NoSuchElementException.
Common pitfalls:
- orElse(expensiveCompute()) always evaluates expensiveCompute() — use orElseGet(() -> ...) for lazy.
- Optional.of(maybeNull) throws NPE — use ofNullable.
Optional is not a universal null replacement — its narrow goal is to make return values explicit about "may be absent".