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

การแปลง Optionals: map และ flatMap

เชื่อมการแปลงด้วย map และ flatMap เพื่อประมวลผลค่าโดยไม่ต้องตรวจสอบ null

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

การแปลง Optional: map และ flatMap

map() จะแปลงค่าภายใน Optional เมื่อมีค่าอยู่ ส่วน flatMap() จะหลีกเลี่ยง Optional ซ้อนกันเมื่อการแปลงนั้นส่งคืน Optional เช่นกัน

map: การแปลงค่า

map(Function) จะใช้ฟังก์ชันกับค่าเมื่อมีค่าอยู่ แล้วห่อหุ้มผลลัพธ์ไว้ใน Optional ใหม่ หากเดิมไม่มีค่าจะส่งคืนค่าว่าง

import java.util.Optional;

Optional<String> name = Optional.of("alice");

Optional<String> upper = name.map(String::toUpperCase);
System.out.println(upper.get()); // ALICE

Optional<Integer> length = name.map(String::length);
System.out.println(length.get()); // 5

// Empty in → empty out
Optional<String> empty = Optional.<String>empty().map(String::toUpperCase);
System.out.println(empty.isPresent()); // false

การเชื่อมการเรียก map

การเรียก map หลายครั้งสามารถเชื่อมการแปลงได้อย่างสะอาด โดยไม่ต้องตรวจสอบค่าว่างซ้อนกัน

import java.util.Optional;

record Address(String city, String country) {}
record User(String name, Address address) {}

User user = new User("Alice", new Address("New York", "US"));

// Chain of maps — safe even if any step returns null
Optional<String> city = Optional.ofNullable(user)
    .map(User::address)
    .map(Address::city)
    .map(String::toUpperCase);

System.out.println(city.orElse("Unknown")); // NEW YORK

flatMap: การหลีกเลี่ยง Optional<Optional>

หากฟังก์ชันการแมปของคุณส่งคืน Optional ให้ใช้ flatMap เพื่อคงไว้เป็น Optional แบบแบน แทนที่จะเป็น Optional<Optional> แบบซ้อนกัน

import java.util.Optional;

record User(String name, Optional<String> email) {}

User u = new User("Bob", Optional.of("bob@example.com"));

// map returns Optional<Optional<String>> — ugly!
Optional<Optional<String>> nested = Optional.of(u).map(User::email);

// flatMap flattens: Optional<String>
Optional<String> flat = Optional.of(u).flatMap(User::email);
System.out.println(flat.get()); // bob@example.com

flatMap ในการใช้งานจริง: สายโซ่ของที่เก็บข้อมูล

รูปแบบที่พบได้บ่อยคือ การเชื่อมการค้นหาจากที่เก็บข้อมูล โดยแต่ละการค้นหาจะส่งคืน Optional

import java.util.Optional;

record Order(long id, long userId, double total) {}
record User(long id, String name, String email) {}

static Optional<Order> findOrder(long orderId) {
    return orderId == 1 ? Optional.of(new Order(1, 42, 99.99)) : Optional.empty();
}
static Optional<User> findUser(long userId) {
    return userId == 42 ? Optional.of(new User(42, "Alice", "a@co.com")) : Optional.empty();
}

Optional<String> email = findOrder(1)
    .flatMap(o -> findUser(o.userId()))
    .map(User::email);

System.out.println(email.orElse("Not found")); // a@co.com

สรุป map กับ flatMap

การเลือกระหว่าง map และ flatMap:

  • ใช้ map เมื่อฟังก์ชันส่งคืนค่าทั่วไป เช่น String หรือชนิดจำนวนเต็ม
  • ใช้ flatMap เมื่อฟังก์ชันส่งคืน Optional
import java.util.Optional;

// map: T → R
Optional.of("hello").map(String::toUpperCase);       // Optional<String>
Optional.of("42").map(Integer::parseInt);             // Optional<Integer>

// flatMap: T → Optional<R>
Optional.of("42").flatMap(s -> {
    try { return Optional.of(Integer.parseInt(s)); }
    catch (NumberFormatException e) { return Optional.empty(); }
}); // Optional<Integer> (not Optional<Optional<Integer>>)

การแยกวิเคราะห์อย่างปลอดภัยด้วย flatMap

รูปแบบที่มีประโยชน์คือ ห่อการดำเนินการที่มีความเสี่ยงและอาจล้มเหลวไว้ใน Optional โดยใช้ flatMap

import java.util.Optional;

static Optional<Integer> tryParseInt(String s) {
    try { return Optional.of(Integer.parseInt(s)); }
    catch (NumberFormatException e) { return Optional.empty(); }
}

Optional<String> input = Optional.of("123");
Optional<Integer> parsed = input.flatMap(JavaOptional::tryParseInt);
System.out.println(parsed.map(n -> n * 2).orElse(-1)); // 246

Optional<String> bad = Optional.of("abc");
Optional<Integer> failed = bad.flatMap(JavaOptional::tryParseInt);
System.out.println(failed.orElse(-1)); // -1

การรวบรวม Optional

กรองและแมปสตรีมของ Optional เพื่อรวบรวมเฉพาะค่าที่มีอยู่

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

List<String> inputs = List.of("10", "abc", "25", "nope", "7");

List<Integer> numbers = inputs.stream()
    .flatMap(s -> {
        try { return Stream.of(Integer.parseInt(s)); }
        catch (NumberFormatException e) { return Stream.empty(); }
    })
    .collect(Collectors.toList());

System.out.println(numbers); // [10, 25, 7]

การเดินทางผ่านออบเจ็กต์ที่ซ้อนกัน

map และ flatMap ช่วยให้เดินทางผ่านกราฟออบเจ็กต์ที่มีความลึกได้อย่างปลอดภัย โดยไม่ต้องตรวจสอบค่าว่างที่ยุ่งยาก

import java.util.Optional;

record Company(String name, Optional<Address> address) {}
record Address(String city, Optional<String> zipCode) {}

Company company = new Company(
    "Acme Corp",
    Optional.of(new Address("Austin", Optional.of("78701")))
);

String zip = Optional.of(company)
    .flatMap(Company::address)
    .flatMap(Address::zipCode)
    .orElse("N/A");

System.out.println(zip); // 78701

map สำหรับการแปลงชนิดข้อมูล

map เหมาะอย่างยิ่งสำหรับการแปลงชนิดของค่าใน Optional เช่น การแยกวิเคราะห์ การจัดรูปแบบ หรือการดึงฟิลด์ย่อย

import java.util.Optional;
import java.time.LocalDate;

// Parse a date from String safely
static Optional<LocalDate> parseDate(String s) {
    try { return Optional.of(LocalDate.parse(s)); }
    catch (Exception e) { return Optional.empty(); }
}

Optional<String> dateStr = Optional.of("2024-06-15");
Optional<Integer> year = dateStr
    .flatMap(JavaOptional::parseDate)
    .map(LocalDate::getYear);

System.out.println(year.orElse(-1)); // 2024

การสร้างไปป์ไลน์

การรวม filter, map และ flatMap ช่วยสร้างไปป์ไลน์ข้อมูลที่สื่อความหมายและจัดการกรณีไม่มีค่าในทุกขั้นตอนได้ โดยไม่ต้องตรวจสอบค่าว่างอย่างชัดเจน

import java.util.Optional;

record Product(String sku, String category, boolean inStock, double price) {}

static Optional<Product> findProduct(String sku) {
    if ("LAPTOP-1".equals(sku))
        return Optional.of(new Product("LAPTOP-1", "Electronics", true, 999.99));
    return Optional.empty();
}

Optional<String> priceTag = findProduct("LAPTOP-1")
    .filter(Product::inStock)
    .filter(p -> p.price() < 2000)
    .map(p -> String.format("%s: $%.2f", p.sku(), p.price()));

System.out.println(priceTag.orElse("Not available")); // LAPTOP-1: $999.99

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

เมื่อใดควรใช้ flatMap แทน map กับ Optional

สรุป: การแปลง Optional: map และ flatMap

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

  • map() จะแปลงค่าเมื่อมีค่าอยู่ และห่อหุ้มผลลัพธ์ไว้ใน Optional
  • flatMap() ใช้กับฟังก์ชันที่ส่งคืน Optional เพื่อหลีกเลี่ยง Optional ที่ซ้อนกัน
  • เชื่อม map หลายรายการสำหรับการแปลงหลายขั้นตอนโดยไม่ต้องตรวจสอบค่าว่าง
  • ใช้ flatMap เพื่อเชื่อมการเรียกที่เก็บข้อมูลหรือบริการซึ่งส่งคืน Optional
  • รวม filter + map + flatMap เพื่อสร้างไปป์ไลน์ข้อมูลที่ปลอดภัยจากค่าว่าง
  • ใช้ flatMap Optional::stream เพื่อรวม Optional เข้ากับไปป์ไลน์สตรีม

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

บทเรียน “การแปลง Optionals: map และ flatMap” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “การแปลง Optionals: map และ flatMap”

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

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

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

บทเรียน “การแปลง Optionals: map และ flatMap” ใช้เวลานานแค่ไหน

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

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

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

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

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