1. 什么是 record

在 Java 开发中,我们经常会写大量的 “数据承载类”,这些类通常只有字段、构造方法、getterequalshashCodetoString 方法,逻辑很少,更多是用来传输数据(DTO、VO 等)。

以往的写法:

public class User {
    private final String name;
    private final int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public boolean equals(Object o) { /* 省略 */ }
    @Override
    public int hashCode() { /* 省略 */ }
    @Override
    public String toString() { /* 省略 */ }
}

啰嗦 + 重复

从 Java 16 起,record 关键字让我们可以用一句话生成这样的类:

public record User(String name, int age) {}

就是这样,Java 会帮我们自动生成:

  • private final 修饰的字段
  • 构造方法
  • getter(方法名就是字段名)
  • equals()hashCode()toString() 方法

2. record 的特性

  1. 不可变性
    record 自动把字段设为 final,对象创建后无法修改字段值。

    User u = new User("Alice", 20);
    // u.name = "Bob"; // ❌ 编译错误
    
  2. 组件(Component)概念
    record 构造器参数列表就定义了这个类的字段,称为 “组件”。
    例如 User(String name, int age) 中,nameage 就是组件。

  3. 自定义构造器
    可以添加额外校验:

    public record User(String name, int age) {
        public User {
            if (age < 0) throw new IllegalArgumentException("Age must be >= 0");
        }
    }
    
  4. 实现接口
    record 可以实现接口:

    public record Point(int x, int y) implements Comparable<Point> {
        @Override
        public int compareTo(Point other) {
            return Integer.compare(this.x, other.x);
        }
    }
    
  5. 不能继承其他类
    所有 record 默认继承 java.lang.Record,不可继承普通类,但可实现接口。


3. 使用场景

  • DTO(数据传输对象)
    简化类定义,让代码更干净。

  • 作为 Map 的 key
    自动生成的 equalshashCode 适合当键。

  • 事件模型(Event Object)
    不可变性 + 自动方法,适合表示一个事件的快照。

  • 返回多值
    record 打包返回多个值:

    public record Result(int code, String message) {}
    

4. 注意事项(坑点)

  1. 不可变 ≠ 真正线程安全
    字段是 final,但如果字段类型本身是可变对象(比如 List),仍然可能被修改:

    public record Person(String name, List<String> hobbies) {}
    var p = new Person("Tom", new ArrayList<>(List.of("reading")));
    p.hobbies().add("coding"); // ✅ 可以改
    

    解决:使用不可变集合包装。

  2. 序列化问题
    某些旧版序列化框架不支持 record,升级框架或使用 JSON 序列化。

  3. 字段命名冲突
    组件名不能和 record 中已有方法名冲突,比如 toString


5. 实战示例

record 创建一个不可变的订单对象

public record Order(String id, String product, int quantity, double price) {
    public Order {
        if (quantity <= 0) throw new IllegalArgumentException("Quantity must be > 0");
        if (price < 0) throw new IllegalArgumentException("Price must be >= 0");
    }

    public double totalPrice() {
        return quantity * price;
    }
}

class Main {
    public static void main(String[] args) {
        Order o = new Order("A001", "Java Book", 2, 39.9);
        System.out.println(o); // Order[id=A001, product=Java Book, quantity=2, price=39.9]
        System.out.println("Total: " + o.totalPrice());
    }
}

输出:

Order[id=A001, product=Java Book, quantity=2, price=39.9]
Total: 79.8

6. 总结

  • record 大幅减少样板代码,非常适合 不可变数据类
  • 默认生成的 equals / hashCode / toString 让数据对象更易用。
  • 注意集合字段不可变性序列化兼容性
  • 在业务中用来定义 DTO、事件对象、多值返回都很高效。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐