Tony Hoare 稱發明 null 為「十億美元錯誤」。Java 8+ 提供 Optional、Objects.requireNonNull、Stream 等工具,能從類型系統與運行時雙重防線消滅 NPE。
// 確定非空
Optional<String> sure = Optional.of("value"); // null → NPE
// 可能為空
Optional<String> maybe = Optional.ofNullable(getName()); // null → Optional.empty()
// 明確為空
Optional<String> empty = Optional.empty();
// 1. 函數式消費(推薦)
maybe.ifPresent(System.out::println);
// 2. 提供默認值
String val = maybe.orElse("default"); // 默認值立即計算
String val = maybe.orElseGet(() -> expensive()); // Supplier 惰性計算
// 3. 異常轉換
String val = maybe.orElseThrow(() -> new IllegalStateException("Missing!"));
// 4. 轉換鏈(避免 isPresent 判斷)
String upper = maybe.map(String::toUpperCase).orElse("DEFAULT");
public String getValSafe(Map<String, String> map, String key) {
Map<String, String> safeMap = Objects.requireNonNull(map, "Map cannot be null");
String safeKey = Objects.requireNonNull(key, "Key cannot be null");
return safeMap.get(safeKey); // 後續可放心調用
}
NullPointerException 並攜帶自定義消息。requireNonNull 行,比手動 if (x == null) throw... 更乾淨。public static <T> T requireNonEmpty(T obj, Predicate<T> emptyCheck, String msg) {
Objects.requireNonNull(obj);
Objects.requireNonNull(emptyCheck);
if (emptyCheck.test(obj)) throw new IllegalArgumentException(msg);
return obj;
}
// 使用示例
String s = requireNonEmpty(input, String::isEmpty, "String empty!");
List<?> list = requireNonEmpty(list, List::isEmpty, "List empty!");
User user = requireNonEmpty(user, u -> u.getName().isEmpty(), "User name empty!");
return Collections.emptyList();list.stream().filter(...).collect(toList()) 空列表返回空列表,無 NPE。| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 方法返回值可能為空 | Optional<T> | 類型聲明意圖,強制調用者處理 |
| 方法參數非空前置條件 | Objects.requireNonNull | 快速失敗,堆棧清晰 |
| 業務規則驗證(非空+非空字符串等) | 自定義 requireNonEmpty | 組合驗證邏輯,復用性強 |
| 集合/Stream 管道 | 返回空集合/空 Stream | 避免 null 傳播,鏈式調用無阻礙 |
Java 8+ 工具箱已足夠強大:Optional 建模「可能缺失」,Objects.requireNonNull 守護「必須存在」,Stream 管道天然免疫空集合。拒絕 if (x != null) 滿屏飛,擁抱函數式空安全編程。