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

เมธอดแบบกำหนดเองบน Records

เพิ่มเมธอดอินสแตนซ์และเมธอดโรงงานแบบ static เพื่อเพิ่มความสามารถให้ records

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

เมธอดแบบกำหนดเองในเรคคอร์ด

เรคคอร์ดสามารถมีเมธอดอินสแตนซ์ เมธอดแบบคงที่ และฟิลด์แบบคงที่ได้ รวมถึงพฤติกรรมใด ๆ ที่คำนวณจากหรือเกี่ยวข้องกับคอมโพเนนต์ของเรคคอร์ด

เมธอดอินสแตนซ์ในเรคคอร์ด

เพิ่มเมธอดให้เรคคอร์ดเพื่อคำนวณค่าที่ได้จากข้อมูลเดิม หรือจัดเตรียมการดำเนินการที่เป็นประโยชน์โดยอาศัยคอมโพเนนต์

record Money(long cents, String currency) {
    public double amount() { return cents / 100.0; }
    public String display() { return String.format("%s %.2f", currency, amount()); }
    public Money add(Money other) {
        if (!currency.equals(other.currency))
            throw new IllegalArgumentException("Currency mismatch");
        return new Money(cents + other.cents, currency);
    }
    public Money multiply(int factor) { return new Money(cents * factor, currency); }
}

Money price = new Money(1999, "USD");
Money tax = new Money(160, "USD");
System.out.println(price.add(tax).display()); // USD 21.59

เมธอดโรงงานแบบคงที่

เมธอดโรงงานแบบคงที่ช่วยให้สร้างเรคคอร์ดด้วยวิธีที่มีชื่อเรียกและอ่านเข้าใจง่าย โดยซ่อนคอนสตรักเตอร์โดยตรงเมื่อการเรียกใช้คอนสตรักเตอร์นั้นไม่ชัดเจน

import java.time.*;

record DateRange(LocalDate start, LocalDate end) {
    DateRange { if (start.isAfter(end)) throw new IllegalArgumentException(); }

    public static DateRange of(LocalDate start, LocalDate end) {
        return new DateRange(start, end);
    }

    public static DateRange thisMonth() {
        LocalDate now = LocalDate.now();
        return new DateRange(now.withDayOfMonth(1),
            now.withDayOfMonth(now.lengthOfMonth()));
    }

    public long durationDays() {
        return ChronoUnit.DAYS.between(start, end) + 1;
    }
}

System.out.println(DateRange.thisMonth().durationDays());

เมธอดตรวจเงื่อนไข

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

record User(String email, String plan, boolean emailVerified) {
    public boolean isPro()      { return "PRO".equals(plan); }
    public boolean canPublish() { return isPro() && emailVerified; }
    public boolean isFree()     { return "FREE".equals(plan); }

    public User withPlan(String newPlan) {
        return new User(email, newPlan, emailVerified); // "update"
    }
}

User u = new User("alice@example.com", "FREE", true);
System.out.println(u.canPublish()); // false
User upgraded = u.withPlan("PRO");
System.out.println(upgraded.canPublish()); // true

เมธอดคัดลอกพร้อมแก้ไข

เนื่องจากเรคคอร์ดเปลี่ยนแปลงไม่ได้ การ "อัปเดต" จึงหมายถึงการสร้างเรคคอร์ดตัวใหม่ เมธอดคัดลอกพร้อมแก้ไขใช้รูปแบบ withX(newValue)

record ServerConfig(String host, int port, boolean ssl, int timeout) {
    public ServerConfig withHost(String h)    { return new ServerConfig(h, port, ssl, timeout); }
    public ServerConfig withPort(int p)       { return new ServerConfig(host, p, ssl, timeout); }
    public ServerConfig withSsl(boolean s)    { return new ServerConfig(host, port, s, timeout); }
    public ServerConfig withTimeout(int t)    { return new ServerConfig(host, port, ssl, t); }
}

ServerConfig dev = new ServerConfig("localhost", 8080, false, 30);
ServerConfig prod = dev
    .withHost("api.example.com")
    .withPort(443)
    .withSsl(true)
    .withTimeout(60);
System.out.println(prod);

เรคคอร์ดที่ใช้งานอินเทอร์เฟซเชิงฟังก์ชัน

เรคคอร์ดที่ใช้งานอินเทอร์เฟซเชิงฟังก์ชันด้วย สามารถส่งผ่านโดยตรงเพื่อใช้แทนแลมบ์ดาได้

import java.util.function.*;

record Validator<T>(Predicate<T> rule, String message)
    implements Predicate<T> {

    public boolean test(T value) { return rule.test(value); }

    public String validate(T value) {
        return test(value) ? null : message;
    }
}

Validator<String> notEmpty = new Validator<>(
    s -> !s.isBlank(), "Must not be blank");
Validator<String> validEmail = new Validator<>(
    s -> s.contains("@"), "Invalid email");

System.out.println(notEmpty.validate(""));        // Must not be blank
System.out.println(validEmail.validate("a@b.c")); // null (valid)

เมธอดรวมข้อมูล

เรคคอร์ดที่เก็บคอลเลกชันสามารถเปิดเผยเมธอดรวมข้อมูลที่ทำงานบน stream ได้

import java.util.*;

record SalesReport(String region, List<Double> dailySales) {
    SalesReport { dailySales = List.copyOf(dailySales); }

    public double total()   { return dailySales.stream().mapToDouble(Double::doubleValue).sum(); }
    public double average() { return dailySales.stream().mapToDouble(Double::doubleValue).average().orElse(0); }
    public double max()     { return dailySales.stream().mapToDouble(Double::doubleValue).max().orElse(0); }
    public int    days()    { return dailySales.size(); }
}

SalesReport r = new SalesReport("North", List.of(1200.0, 1450.0, 980.0, 1600.0));
System.out.printf("Total: %.0f, Avg: %.0f, Max: %.0f%n", r.total(), r.average(), r.max());
// Total: 5230, Avg: 1307, Max: 1600

การเขียนทับ toString แบบกำหนดเอง

แม้ toString ที่สร้างโดยอัตโนมัติจะมีประโยชน์สำหรับการดีบัก แต่คุณอาจต้องการเขียนทับเพื่อใช้แสดงผลแก่ผู้ใช้

record Temperature(double celsius) {
    @Override
    public String toString() {
        return String.format("%.1f\u00b0C / %.1f\u00b0F",
            celsius, toFahrenheit());
    }

    public double toFahrenheit() { return celsius * 9.0/5.0 + 32; }
    public double toKelvin()     { return celsius + 273.15; }

    public static Temperature fromFahrenheit(double f) {
        return new Temperature((f - 32) * 5.0/9.0);
    }
}

Temperature boiling = new Temperature(100.0);
System.out.println(boiling); // 100.0°C / 212.0°F

เรคคอร์ดในฐานะออบเจ็กต์ค่าใน DDD

ใน DDD เรคคอร์ดสอดคล้องกับออบเจ็กต์ค่า (Value Objects) ได้อย่างเป็นธรรมชาติ ซึ่งเป็นชนิดข้อมูลที่กำหนดโดยแอตทริบิวต์ ไม่ใช่ด้วยอัตลักษณ์

// Value objects in a banking domain
record AccountId(String value) {
    AccountId { if (!value.matches("ACC-[0-9]{8}")) throw new IllegalArgumentException(); }
}
record Amount(long cents, String currency) {
    public static Amount usd(long cents) { return new Amount(cents, "USD"); }
    public boolean isPositive() { return cents > 0; }
}
record TransactionId(java.util.UUID value) {
    public static TransactionId generate() {
        return new TransactionId(java.util.UUID.randomUUID());
    }
}

AccountId id = new AccountId("ACC-12345678");
Amount amount = Amount.usd(5000);
TransactionId txId = TransactionId.generate();
System.out.println(id + " " + amount + " " + txId);

การเปรียบเทียบเรคคอร์ดด้วย Comparator

เรคคอร์ดสามารถจัดเรียงได้โดยใช้ Comparator.comparing ร่วมกับการอ้างอิงเมธอดที่ชี้ไปยังเมธอดเข้าถึงข้อมูล

import java.util.*;

record Employee(String name, String dept, double salary) {}

List<Employee> employees = List.of(
    new Employee("Alice",   "Engineering", 95000),
    new Employee("Bob",     "Marketing",   72000),
    new Employee("Charlie", "Engineering", 105000),
    new Employee("Diana",   "Marketing",   81000)
);

// Sort by dept, then salary descending
employees.stream()
    .sorted(Comparator.comparing(Employee::dept)
        .thenComparing(Comparator.comparingDouble(Employee::salary).reversed()))
    .forEach(e -> System.out.printf("%-10s %-15s %.0f%n",
        e.name(), e.dept(), e.salary()));

เมธอดยูทิลิตีแบบคงที่ในเรคคอร์ด

เรคคอร์ดสามารถมีเมธอดแบบคงที่ที่ทำงานกับอินสแตนซ์ของเรคคอร์ดหลายตัว ซึ่งมีประโยชน์สำหรับการแปลงและการเปรียบเทียบ

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

record Product(String id, String category, double price) {
    public static Map<String, DoubleSummaryStatistics> statsByCategory(
            List<Product> products) {
        return products.stream().collect(
            Collectors.groupingBy(Product::category,
                Collectors.summarizingDouble(Product::price)));
    }

    public static Optional<Product> cheapest(List<Product> products) {
        return products.stream()
            .min(Comparator.comparingDouble(Product::price));
    }
}

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

คุณต้องการ "อัปเดต" ฟิลด์ของเรคคอร์ด แนวทางที่ถูกต้องคืออะไร

สรุปทบทวน: เมธอดแบบกำหนดเองในเรคคอร์ด

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

  • เรคคอร์ดสามารถมีเมธอดอินสแตนซ์ เมธอดแบบคงที่ และฟิลด์แบบคงที่
  • เพิ่มคุณสมบัติที่คำนวณได้ในรูปแบบเมธอด เช่น area(), ผลรวม() และ display()
  • ใช้เมธอดโรงงานแบบคงที่เพื่อสร้างคอนสตรักเตอร์ที่มีชื่ออ่านเข้าใจง่าย
  • ใช้งานเมธอดคัดลอกพร้อมแก้ไข (withX) สำหรับการอัปเดตแบบเปลี่ยนแปลงไม่ได้
  • เขียนทับ toString() เพื่อแสดงผลแก่ผู้ใช้ แทนการแสดงผลสำหรับการดีบัก
  • เรคคอร์ดสอดคล้องกับออบเจ็กต์ค่าใน DDD ได้อย่างเป็นธรรมชาติ

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

บทเรียน “เมธอดแบบกำหนดเองบน Records” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “เมธอดแบบกำหนดเองบน Records”

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

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

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

บทเรียน “เมธอดแบบกำหนดเองบน Records” ใช้เวลานานแค่ไหน

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

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

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

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

  1. เริ่มต้นใช้งาน Records
  2. ตัวสร้างแบบกระชับและการตรวจสอบความถูกต้อง
  3. เมธอดแบบกำหนดเองบน Records
  4. Records กับคลาสและ Lombok
← กลับไปที่ Java Academy