Java Records: Immutable Data Classes (Java 16+)

#java13 เม.ย. 2569

Records คืออะไร

Records (Java 16+) เป็น special class สำหรับ immutable data ที่ Java สร้าง constructor, getters, equals, hashCode, toString ให้อัตโนมัติ

สร้าง Record

// แทนที่ class ยาวๆ
public record Point(int x, int y) {}

// ใช้งาน
Point p = new Point(3, 4);
System.out.println(p.x());    // 3
System.out.println(p.y());    // 4
System.out.println(p);        // Point[x=3, y=4]

Point p2 = new Point(3, 4);
System.out.println(p.equals(p2));  // true

Record กับ Validation

public record Person(String name, int age) {
    // Compact constructor
    public Person {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("ชื่อต้องไม่ว่าง");
        }
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("อายุไม่ถูกต้อง");
        }
        name = name.trim();  // normalize
    }
}

Person p = new Person("  สมชาย  ", 25);
System.out.println(p.name());  // "สมชาย" (trimmed)

Record กับ Methods

public record Money(double amount, String currency) {
    // Custom method
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("สกุลเงินไม่ตรงกัน");
        }
        return new Money(this.amount + other.amount, this.currency);
    }

    public Money multiply(double factor) {
        return new Money(this.amount * factor, this.currency);
    }

    public boolean isPositive() {
        return amount > 0;
    }
}

Money price = new Money(100.0, "THB");
Money tax = new Money(7.0, "THB");
Money total = price.add(tax);
System.out.println(total);  // Money[amount=107.0, currency=THB]

Record กับ Interface

public interface Describable {
    String describe();
}

public record Product(String name, double price) implements Describable {
    @Override
    public String describe() {
        return name + " ราคา " + price + " บาท";
    }
}

Generic Record

public record Pair<A, B>(A first, B second) {
    public Pair<B, A> swap() {
        return new Pair<>(second, first);
    }
}

Pair<String, Integer> pair = new Pair<>("hello", 42);
Pair<Integer, String> swapped = pair.swap();

เปรียบเทียบกับ Class เดิม

// ก่อน Records - ~50 บรรทัด
public class PersonOld {
    private final String name;
    private final int age;
    // constructor, getters, equals, hashCode, toString...
}

// หลัง Records - 1 บรรทัด
public record Person(String name, int age) {}

สรุป

Records ลด boilerplate code ได้มากสำหรับ data classes เหมาะกับ DTOs, Value Objects, API responses ที่ต้องการ immutability ครับ