Java Record

Java 14 引入(Java 16 正式),Record 是一種簡潔的方式來建立不可變的資料類別。

基本語法

// 定義 Record
public record Person(String name, int age) {}

// 使用
Person person = new Person("Alice", 25);
System.out.println(person.name());  // Alice
System.out.println(person.age());   // 25

Record 自動提供

Record 會自動產生:

  • 所有欄位的 getter 方法(沒有 get 前綴)
  • equals() 方法
  • hashCode() 方法
  • toString() 方法
  • 全參數建構子

等同於傳統類別

// 這個 Record
public record Person(String name, int age) {}

// 等同於以下傳統類別
public final class Person {
    private final String name;
    private final int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String name() { return name; }
    public int age() { return age; }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    
    @Override
    public String toString() {
        return "Person[name=" + name + ", age=" + age + "]";
    }
}

自訂建構子

緊湊建構子

public record Person(String name, int age) {
    // 緊湊建構子:不需要參數列表
    public Person {
        // 驗證
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("名稱不能為空");
        }
        if (age < 0) {
            throw new IllegalArgumentException("年齡不能為負數");
        }
        // 正規化
        name = name.trim();
    }
}

自訂建構子

public record Point(int x, int y) {
    // 原點建構子
    public Point() {
        this(0, 0);
    }
    
    // 只有 x 的建構子
    public Point(int x) {
        this(x, 0);
    }
}

Point origin = new Point();      // (0, 0)
Point xOnly = new Point(5);      // (5, 0)
Point full = new Point(3, 4);    // (3, 4)

新增方法

public record Rectangle(double width, double height) {
    // 計算面積
    public double area() {
        return width * height;
    }
    
    // 計算周長
    public double perimeter() {
        return 2 * (width + height);
    }
    
    // 靜態工廠方法
    public static Rectangle square(double side) {
        return new Rectangle(side, side);
    }
}

Rectangle rect = new Rectangle(3, 4);
System.out.println(rect.area());       // 12.0
System.out.println(rect.perimeter());  // 14.0

Rectangle square = Rectangle.square(5);

實作介面

public interface Printable {
    void print();
}

public record Book(String title, String author) implements Printable {
    @Override
    public void print() {
        System.out.println(title + " by " + author);
    }
}

Record 的限制

  • 所有欄位都是 final(不可變)
  • 不能繼承其他類別
  • 不能宣告實例變數
  • final 類別,不能被繼承
// 錯誤:不能繼承
// public record Employee(String name) extends Person { }

// 錯誤:不能有實例變數
// public record Person(String name) {
//     private int age;  // 錯誤
// }

實際應用

DTO(資料傳輸物件)

public record UserDTO(Long id, String name, String email) {}

// API 回應
public record ApiResponse<T>(int code, String message, T data) {}

複合鍵

public record CompositeKey(String part1, String part2) {}

Map<CompositeKey, String> map = new HashMap<>();
map.put(new CompositeKey("a", "b"), "value");

回傳多個值

public record MinMax(int min, int max) {}

public MinMax findMinMax(int[] array) {
    int min = Arrays.stream(array).min().orElse(0);
    int max = Arrays.stream(array).max().orElse(0);
    return new MinMax(min, max);
}

MinMax result = findMinMax(new int[]{3, 1, 4, 1, 5});
System.out.println(result.min());  // 1
System.out.println(result.max());  // 5

設定類別

public record DatabaseConfig(String url, String username, String password) {}

public record ServerConfig(String host, int port, DatabaseConfig database) {}

Record 和模式匹配 (Java 19+)

public record Point(int x, int y) {}

// 模式匹配
Object obj = new Point(3, 4);

if (obj instanceof Point(int x, int y)) {
    System.out.println("x = " + x + ", y = " + y);
}

// switch 中使用
String result = switch (obj) {
    case Point(int x, int y) -> "Point at (" + x + ", " + y + ")";
    default -> "Unknown";
};

Record vs Class

特性RecordClass
可變性不可變可變
繼承不可繼承可繼承
樣板程式碼
用途資料載體一般用途