Java Optional.orElseGet: เรียก fallback เฉพาะตอนค่าหลักหาย
fallback บางตัวไม่ได้เป็นค่าคงที่ แต่อ่านไฟล์ เรียก service หรือเพิ่ม metric ถ้าใช้ orElse(expensiveCall()) ฟังก์ชันนั้นถูกประเมินก่อนแม้ Optional มีค่าแล้ว Optional.orElseGet รับ Supplier และเรียกเฉพาะตอนว่างครับ
WHY: อย่าเปิดครัวสำรองถ้าจานหลักพร้อมแล้ว
ร้านมีอาหารจานหลักอยู่บนเคาน์เตอร์ จึงไม่ควรปลุกพนักงานไปทำจานสำรองทุกครั้งแล้วทิ้ง orElseGet เหมือนกริ่งที่ดังเฉพาะเมื่อถาดหลักว่าง ลดทั้งต้นทุนและ side effect ที่ไม่จำเป็น
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
static String resolve(Optional<String> configured, AtomicInteger fallbackCalls) {
return configured.orElseGet(() -> {
fallbackCalls.incrementAndGet();
return "safe-default";
});
}
public static void main(String[] args) {
AtomicInteger calls = new AtomicInteger();
assert resolve(Optional.of("configured"), calls).equals("configured");
assert calls.get() == 0;
assert resolve(Optional.empty(), calls).equals("safe-default");
assert calls.get() == 1;
System.out.println("lazy fallback verified");
}
}
HOW: lazy ไม่ได้แปลว่า retry หรือ cache
Supplier ถูกเรียกทุกครั้งที่ method ทำงานกับ Optional ว่าง มันไม่ได้ memoize ผลและไม่ retry เมื่อ exception เกิด ถ้า fallback เป็น network call ต้องกำหนด timeout, retry budget และ caching ที่ layer เหมาะสม อย่าซ่อน I/O ขนาดใหญ่ไว้ใน lambda จน reviewer มองไม่เห็นต้นทุน
Optional ควรใช้สื่อค่าที่อาจไม่มีใน return type มากกว่าวางเป็น field ทุกแห่ง ถ้า “ไม่มีค่า” เป็น error ที่ต้องอธิบาย ใช้ orElseThrow พร้อม exception ระดับโดเมนจะชัดกว่า และถ้า fallback เป็น literal ราคาถูก orElse อ่านง่ายกว่าโดยไม่มีปัญหา
Recap
- ใช้
orElseGetเมื่อ fallback แพงหรือมี side effect - ใช้
orElseกับค่าคงที่ราคาถูก - อย่าซ่อน network/file I/O โดยไม่ตั้ง timeout และ policy
- จำว่า Supplier ไม่ cache และไม่ retry
- ใช้
orElseThrowเมื่อค่าหายคือ error