NullPointerException 是 Java 開發中最常見的運行時異常。Tony Hoare 稱發明 null 為「十億美元錯誤」。Java 8 引入 java.util.Optional<T>,提供函數式風格的空值處理方案,本文深入講解核心 API、實戰模式與避坑指南。
Optional.empty())。Optional.of(value)(值非 null)、Optional.ofNullable(value)(允許 null)、Optional.empty()。| 方法 | 說明 |
|---|---|
isPresent() | 有值返回 true |
ifPresent(Consumer) | 有值執行消費邏輯 |
orElse(T) | 有值返回值,否則返回預設值(立即求值) |
orElseGet(Supplier) | 有值返回值,否則執行 Supplier(延遲求值) |
orElseThrow(Supplier) | 無值拋出指定異常 |
map(Function) | 值存在時轉換,返回新 Optional |
flatMap(Function) | 值存在時轉換為 Optional,扁平化 |
filter(Predicate) | 值存在且滿足條件保留,否則返回 empty |
if (obj != null)// 傳統
String city = null;
if (person != null) {
Address addr = person.getAddress();
if (addr != null) city = addr.getCity();
}
// Optional 鏈式
String city = Optional.ofNullable(person)
.map(Person::getAddress)
.map(Address::getCity)
.orElse("Unknown");
Optional.ofNullable(user)
.map(User::getEmail)
.filter(email -> email.contains("@"))
.ifPresent(emailService::send);
// 昂貴操作僅在需要時執行
String config = Optional.ofNullable(System.getenv("MY_CONFIG"))
.orElseGet(() -> computeDefaultConfig()); // 不會每次都算
User user = userRepo.findById(id)
.orElseThrow(() -> new BusinessException("USER_NOT_FOUND", id));
// 過濾列表中非空元素並提取屬性 Listnames = users.stream() .flatMap(user -> Optional.ofNullable(user.getName()).stream()) .collect(Collectors.toList()); // 或 Java 9+ Optional::stream List names = users.stream() .map(User::getName) .flatMap(Optional::stream) .collect(Collectors.toList());
Optional 用於欄位、方法參數、序列化場景(增加複雜度)。orElseGet 代替 orElse 避免不必要計算。opt.get() 直接取值(可能拋 NoSuchElementException),用 orElseThrow。Optional<List>;單值查詢回傳 Optional。Optional 有微小物件分配開銷,極高頻熱路徑可考慮傳統 if-null。Optional 不是銀彈,但能將「可能為空」的語義顯式化,配合函數式 API 寫出更安全、可讀的代碼。核心原則:用於回傳值,不用於參數/欄位;鏈式處理,避免 get();善用 orElseGet/orElseThrow。結合 Java 8+ Stream、Lambda,構建現代化、健壯的 Java 代碼庫。