Java Optional
Optional 是 Java 8 引入的容器類別,用來處理可能為 null 的值,避免 NullPointerException。
建立 Optional
// 包含值
Optional<String> opt1 = Optional.of("Hello");
// 空的 Optional
Optional<String> opt2 = Optional.empty();
// 值可能為 null
String str = null;
Optional<String> opt3 = Optional.ofNullable(str); // 等同於 empty()
Optional<String> opt4 = Optional.ofNullable("Hello"); // 等同於 of()
Optional.of(null) 會拋出 NullPointerException。檢查和取值
Optional<String> opt = Optional.of("Hello");
// 檢查是否有值
if (opt.isPresent()) {
System.out.println(opt.get());
}
// 如果有值就執行
opt.ifPresent(s -> System.out.println(s));
opt.ifPresent(System.out::println);
// Java 9+ 如果有值或沒有值
opt.ifPresentOrElse(
s -> System.out.println("有值:" + s),
() -> System.out.println("沒有值")
);
取得值或預設值
Optional<String> opt = Optional.empty();
// orElse:取得值或預設值
String result1 = opt.orElse("預設值"); // "預設值"
// orElseGet:取得值或計算預設值(惰性)
String result2 = opt.orElseGet(() -> "計算的預設值");
// orElseThrow:取得值或拋出例外
String result3 = opt.orElseThrow(); // NoSuchElementException
String result4 = opt.orElseThrow(() -> new RuntimeException("沒有值"));
orElse vs orElseGet
// orElse:總是會執行預設值計算
String result1 = opt.orElse(expensiveOperation()); // 即使有值也會執行
// orElseGet:只在需要時才計算
String result2 = opt.orElseGet(() -> expensiveOperation()); // 有值不會執行
轉換
map
Optional<String> opt = Optional.of("Hello");
Optional<Integer> length = opt.map(String::length); // Optional[5]
Optional<String> upper = opt.map(String::toUpperCase); // Optional[HELLO]
// 空的 Optional
Optional<String> empty = Optional.empty();
Optional<Integer> emptyLen = empty.map(String::length); // Optional.empty
flatMap
用於回傳 Optional 的函數:
Optional<String> opt = Optional.of("Hello");
// map 會產生 Optional<Optional<Integer>>
Optional<Optional<Integer>> nested = opt.map(s -> Optional.of(s.length()));
// flatMap 會攤平
Optional<Integer> flat = opt.flatMap(s -> Optional.of(s.length()));
過濾
Optional<Integer> opt = Optional.of(10);
Optional<Integer> filtered = opt.filter(n -> n > 5); // Optional[10]
Optional<Integer> filtered2 = opt.filter(n -> n > 20); // Optional.empty
實際應用
避免 null 檢查
// 傳統方式
public String getCity(Person person) {
if (person != null) {
Address address = person.getAddress();
if (address != null) {
return address.getCity();
}
}
return "Unknown";
}
// 使用 Optional
public String getCityOptional(Person person) {
return Optional.ofNullable(person)
.map(Person::getAddress)
.map(Address::getCity)
.orElse("Unknown");
}
作為回傳值
public Optional<User> findUserById(String id) {
User user = database.find(id);
return Optional.ofNullable(user);
}
// 使用
Optional<User> user = findUserById("123");
user.ifPresent(u -> System.out.println(u.getName()));
String name = user.map(User::getName).orElse("Anonymous");
Stream 整合
List<String> names = Arrays.asList("Alice", null, "Bob", null, "Charlie");
// 過濾 null
List<String> nonNull = names.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 使用 Optional
List<String> result = names.stream()
.map(Optional::ofNullable)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
// Java 9+ flatMap + stream
List<String> result2 = names.stream()
.flatMap(s -> Optional.ofNullable(s).stream())
.collect(Collectors.toList());
Optional 的方法總覽
| 方法 | 說明 |
|---|---|
of(value) | 建立(值不能為 null) |
ofNullable(value) | 建立(值可以為 null) |
empty() | 建立空的 Optional |
isPresent() | 是否有值 |
isEmpty() | 是否為空(Java 11+) |
get() | 取得值 |
orElse(default) | 值或預設值 |
orElseGet(supplier) | 值或計算預設值 |
orElseThrow() | 值或拋出例外 |
ifPresent(consumer) | 如果有值就執行 |
map(function) | 轉換 |
flatMap(function) | 轉換並攤平 |
filter(predicate) | 過濾 |
or(supplier) | 值或另一個 Optional(Java 9+) |
stream() | 轉為 Stream(Java 9+) |
最佳實踐
- 不要作為欄位:Optional 適合作為回傳值
- 不要作為參數:使用多載方法代替
- 不要用 get() 而沒有 isPresent()
- 優先使用 orElse 或 map
// 不好
Optional<String> opt = getOptional();
if (opt.isPresent()) {
return opt.get();
}
return "default";
// 好
return getOptional().orElse("default");