Java Optional.or: ต่อ Fallback แบบ Lazy โดยไม่แกะ Optional กลางทาง
fallback chain เช่น cache → database → default มักถูกเขียนด้วย isPresent หลายชั้น หรือใช้ orElseGet เร็วเกินไปจนค่ากลางหลุดจาก Optional Optional.or รับ supplier ที่คืน Optional อีกก้อน จึงต่อแหล่งข้อมูลหลายชั้นได้โดยยัง lazy และยังไม่บังคับเลือกค่าปลายทางครับ
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
static Optional<String> resolve(
Optional<String> cache,
AtomicInteger databaseCalls) {
return cache.or(() -> {
databaseCalls.incrementAndGet();
return Optional.of("from-database");
});
}
public static void main(String[] args) {
AtomicInteger calls = new AtomicInteger();
String cached = resolve(Optional.of("from-cache"), calls).orElseThrow();
assert cached.equals("from-cache");
assert calls.get() == 0 : "fallback must stay lazy";
String loaded = resolve(Optional.empty(), calls).orElseThrow();
assert loaded.equals("from-database");
assert calls.get() == 1;
System.out.println("fallback chain verified");
}
}
ความต่างเล็กที่มีผลใหญ่
orElse(value) ประเมิน value ก่อนเรียก จึงไม่เหมาะกับงานแพง orElseGet(supplier) lazy แต่จบ chain ด้วยค่า T ขณะที่ or(supplier) lazy และยังคืน Optional<T> เหมาะเมื่อ fallback เองอาจไม่มีข้อมูล หรือเราต้องการต่อแหล่งที่สาม
supplier ห้ามคืน null เพราะผิดสัญญา ถ้าแหล่งข้อมูลล้มเหลวอย่ากลืน exception แล้วแปลงเป็น empty โดยอัตโนมัติ—“ไม่มีข้อมูล” กับ “ฐานข้อมูลล่ม” เป็นคนละสถานะ อาจใช้ result type ของโดเมนหรือปล่อย exception ที่แปลความหมายได้
chain ที่อ่านสวยอาจซ่อน I/O หลายรอบ ควรใส่ metric แยก hit/miss/failed และกำหนด timeout ของแต่ละแหล่ง หาก cache miss หลายคำขอพร้อมกัน fallback อาจยิงฐานข้อมูลซ้ำ Optional.or ไม่ได้ทำ request coalescing ให้เรา
Recap
-
orรักษา chain เป็น Optional และเรียก fallback แบบ lazy - แยก empty ออกจาก failure ของแหล่งข้อมูล
- วัด hit/miss และจำกัด I/O ที่ supplier
- ใช้
orElseGetเมื่อปลายทางต้องการค่า ไม่ใช่ Optional ต่อครับ