0Pricing
Java Academy · บทเรียน

เหตุผลที่มี Optional

ทำความเข้าใจปัญหา null และวิธีที่ Optional ใช้แทนการไม่มีค่าอย่างปลอดภัย

เหตุผลที่มี Optional เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

เหตุผลที่มี Optional

คลาส Optional ใช้แทนกรณีที่ค่าอาจไม่มีอยู่ โดยบังคับให้ผู้เรียกจัดการกรณีที่ไม่มีค่าอย่างชัดเจน จึงช่วยลด NullPointerExceptions

ปัญหาค่าว่าง

การส่งคืนค่าว่างจากเมธอดเพื่อบ่งบอกว่าไม่มีค่าเป็นเรื่องอันตราย ผู้เรียกอาจลืมตรวจสอบ จนทำให้เกิด NPE หลายกรณีในส่วนลึกของลำดับการเรียก

// Old way — null return invites NPE
static String findEmail(int userId) {
    if (userId == 1) return "alice@example.com";
    return null; // caller might forget to check!
}

String email = findEmail(99);
System.out.println(email.toUpperCase()); // NullPointerException!

Optional ในฐานะชนิดค่าที่ส่งคืน

การส่งคืน Optional<T> ทำให้ระบุอย่างชัดเจนในลายเซ็นของเมธอดว่าค่าอาจไม่มีอยู่ ผู้เรียกจึงต้องจัดการกรณีนี้

import java.util.Optional;

static Optional<String> findEmail(int userId) {
    if (userId == 1) return Optional.of("alice@example.com");
    return Optional.empty();
}

// Caller must handle the Optional
Optional<String> result = findEmail(99);
if (result.isPresent()) {
    System.out.println(result.get().toUpperCase());
} else {
    System.out.println("User not found");
}

Optional ไม่ใช่คำตอบสำหรับทุกปัญหา

ห้ามใช้ Optional เป็นชนิดของฟิลด์ พารามิเตอร์ของเมธอด หรือสมาชิกภายในคอลเลกชัน กรณีใช้งานหลักคือใช้เป็นชนิดค่าที่ส่งคืนจากเมธอดซึ่งอาจไม่ส่งคืนค่า

// WRONG: Optional as field
class User {
    private Optional<String> nickname; // bad practice
}

// WRONG: Optional as parameter
void save(Optional<String> name) {} // just use overloading

// CORRECT: Optional only as return type
static Optional<User> findById(long id) {
    // returns Optional.empty() if not found
    return database.findById(id);
}

Optional กับข้อยกเว้น

ใช้ Optional สำหรับกรณีที่คาดหมายได้ว่าอาจไม่มีค่า เช่น ไม่พบผู้ใช้หรือการกำหนดค่าว่าง ใช้ข้อยกเว้นสำหรับความล้มเหลวที่ไม่คาดหมาย เช่น ข้อผิดพลาดในการเชื่อมต่อฐานข้อมูลหรือรูปแบบไม่ถูกต้อง

// Good use of Optional: user might not exist
static Optional<User> findUser(String email) { ... }

// Good use of exception: invalid operation
static User createUser(String email) {
    if (!email.contains("@"))
        throw new IllegalArgumentException("Invalid email");
    // ...
}

// Rule of thumb:
// Optional = absence is a normal, expected outcome
// Exception = something went wrong that should not happen

Optional ในคอลเลกชัน

อย่าใส่ Optional ไว้ภายในคอลเลกชัน แต่ให้กรองค่าที่ไม่มีอยู่ออกโดยใช้สตรีม

import java.util.*;
import java.util.stream.*;

List<String> ids = List.of("user:1", "cache:miss", "user:2");

// BAD: List<Optional<User>>
// GOOD: filter optionals out of streams
List<String> users = ids.stream()
    .map(id -> id.startsWith("user:") ? Optional.of(id) : Optional.<String>empty())
    .flatMap(Optional::stream) // Java 9+: flatMap empty optionals
    .collect(Collectors.toList());
System.out.println(users); // [user:1, user:2]

การออกแบบด้วย Optional

การออกแบบที่คำนึงถึง Optional ทำให้ส่วนติดต่อโปรแกรมระบุพฤติกรรมการส่งคืนค่าได้อย่างตรงไปตรงมามากขึ้น ต่อไปนี้คือตัวอย่างรูปแบบที่เก็บข้อมูลด้วย Optional

import java.util.*;

interface UserRepository {
    Optional<User> findById(long id);
    Optional<User> findByEmail(String email);
    List<User> findAll(); // no Optional — returns empty list if none
}

record User(long id, String name, String email) {}

// The caller sees immediately that the result might be absent
UserRepository repo = /* inject */;
repo.findByEmail("alice@example.com")
    .ifPresentOrElse(
        u -> System.out.println("Found: " + u.name()),
        () -> System.out.println("Not found")
    );

Optional กับสตรีม

Optional ทำงานร่วมกับสตรีมได้ดีผ่าน stream() (Java 9) ซึ่งสร้างสตรีมที่มีสมาชิก 0 หรือ 1 รายการ

import java.util.*;
import java.util.stream.*;

List<Optional<String>> maybeNames = List.of(
    Optional.of("Alice"),
    Optional.empty(),
    Optional.of("Bob"),
    Optional.empty(),
    Optional.of("Charlie")
);

List<String> present = maybeNames.stream()
    .flatMap(Optional::stream) // Java 9+
    .collect(Collectors.toList());
System.out.println(present); // [Alice, Bob, Charlie]

Optional.of กับ ofNullable

Optional.of(value) จะโยน NPE หาก value เป็นค่าว่าง ส่วน Optional.ofNullable(value) จะห่อหุ้มค่าว่างเป็น Optional ว่างอย่างปลอดภัย

import java.util.Optional;

String name = null;

// Optional.of(null) throws NullPointerException!
try {
    Optional<String> bad = Optional.of(name);
} catch (NullPointerException e) {
    System.out.println("NPE from Optional.of(null)");
}

// Optional.ofNullable(null) returns empty safely
Optional<String> safe = Optional.ofNullable(name);
System.out.println(safe.isPresent()); // false
System.out.println(safe.isEmpty());   // true (Java 11+)

หมายเหตุเกี่ยวกับประสิทธิภาพ

Optional ห่อหุ้มค่าไว้ในออบเจ็กต์ จึงมีต้นทุนการจัดสรรหน่วยความจำเล็กน้อยต่อ Optional หนึ่งรายการ หากอยู่ในเส้นทางการทำงานที่มีการสร้างหลายล้านรายการต่อวินาที ควรพิจารณาใช้การตรวจสอบค่าว่างแทน แต่สำหรับโค้ดบริการทั่วไป Optional เป็นตัวเลือกที่ดีกว่าในด้านความอ่านง่าย

// In hot loops (millions/second), null check might be preferred:
// String result = findFastPath(id); // returns null or value
// if (result != null) use(result);

// For typical service/repository code:
// Optional<String> result = findValue(id);
// result.ifPresent(v -> use(v));

// Modern JVMs are good at escape analysis — many Optional allocations
// are stack-allocated and cause no heap pressure.

บริบททางประวัติศาสตร์: ความผิดพลาดมูลค่าพันล้านดอลลาร์ของโทนี ฮอร์

โทนี ฮอร์ ผู้คิดค้นการอ้างอิงค่าว่างในปี 1965 เรียกสิ่งนี้ว่า ความผิดพลาดมูลค่าพันล้านดอลลาร์ของเขา เพราะนำไปสู่ความล้มเหลวของระบบนับไม่ถ้วน Optional คือคำตอบของ Java ในการทำให้การไม่มีค่าชัดเจนและปลอดภัย

// Java's Optional was inspired by functional languages:
// Haskell's Maybe, Scala's Option, Rust's Option<T>

// The goal: make the type system express that absence is possible
// Instead of relying on documentation or developer vigilance

// Before Optional:
// String email = user.getEmail(); // is this null? Check the docs!

// With Optional:
// Optional<String> email = user.getEmail(); // type says: might be absent

ตรวจสอบความเข้าใจ

เมื่อใดควรใช้ Optional เป็นชนิดค่าที่ส่งคืน

สรุป: เหตุผลที่มี Optional

ประเด็นสำคัญ:

  • Optional ใช้แทนค่าที่อาจมีหรือไม่มี ทำให้การไม่มีค่าชัดเจนในชนิดข้อมูล
  • ใช้ Optional.of() เมื่อค่าไม่เป็นค่าว่างแน่นอน และใช้ ofNullable() เมื่อค่าอาจเป็นค่าว่าง
  • ใช้ Optional.empty() เพื่อส่งคืนกรณีที่ไม่มีค่า
  • อย่าใช้ Optional เป็นฟิลด์ พารามิเตอร์ของเมธอด หรือสมาชิกภายในคอลเลกชัน
  • Optional ทำงานร่วมกับสตรีมผ่าน stream() (Java 9 ขึ้นไป)
  • เลือกใช้ Optional แทนการส่งคืนค่าว่างเพื่อการออกแบบส่วนติดต่อโปรแกรมที่ดีขึ้น และเลือกใช้ข้อยกเว้นสำหรับความล้มเหลว

คำถามที่พบบ่อย

บทเรียน “เหตุผลที่มี Optional” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “เหตุผลที่มี Optional” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “เหตุผลที่มี Optional”

ทำความเข้าใจปัญหา null และวิธีที่ Optional ใช้แทนการไม่มีค่าอย่างปลอดภัย คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “เหตุผลที่มี Optional” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เหตุผลที่มี Optional
  2. การสร้างและตรวจสอบ Optionals
  3. การแปลง Optionals: map และ flatMap
  4. แนวทางปฏิบัติที่ดีสำหรับ Optional
← กลับไปที่ Java Academy