图片

作为一个 Java 开发者,请闭上眼睛,回想一下你曾写过的那些 User.javaOrderDTO.javaProductVO.java... 你看到的,是不是像下面这样,一片望不到尽头的“代码之海”?

public class User {
    private Long id;
    private String username;
    private String email;
public User(Long id, String username, String email) {
    this.id = id;
    this.username = username;
    this.email = email;
}
// Getters and Setters...
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
// ... (username 和 email 的 getter/setter) ...
@Override
public boolean equals(Object o) {
    // ... 20行复杂的 equals 实现 ...
}
@Override
public int hashCode() {
    // ... 10行复杂的 hashCode 实现 ...
}
@Override
public String toString() {
    // ... 10行复杂的 toString 实现 ...
}
}

这就是我们常说的样板代码 (Boilerplate Code)。这些 gettersetterequalshashCodetoString 方法,几乎占据了数据对象 90% 的代码量。它们是“噪音”,淹没了这个类真正的核心——它所承载的数据。

多年来,我们默默忍受着这一切,但 Java 语言本身,其实早已悄悄地为我们提供了终结这场“噩梦”的“语法糖”。如果你还在手动编写这些样板代码,那么你真的可能错过了 Java 近年来最棒的一次进化。

“现在时”:Java Record - 优雅的数据载体

从 Java 16 开始,一个名为 Record 的新类型正式登陆。它的诞生,只有一个目标:成为一个纯粹、透明、不可变的数据载体。

还记得上面那个臃肿的 User 类吗?使用 Record,我们可以用一行代码来定义它:

public record User(Long id, String username, String email) {}

是的,你没有看错,就这么多。这一行代码,编译器会自动为你生成:

  • • 所有参数的 private final 字段。

  • • 一个包含了所有参数的全参构造函数

  • • 所有字段的 public “访问器”方法(例如 user.id(),注意没有 get 前缀)。

  • • 一个被正确实现的、包含了所有字段的 equals() 方法。

  • • 一个被正确实现的、包含了所有字段的 hashCode() 方法。

  • • 一个被正确实现的、包含了所有字段和其名称的 toString() 方法。

Record 的核心特性:

  1. 1. 简洁 (Concise): 极大地减少了样板代码,让类的核心意图(承载数据)一目了然。

  2. 2. 不可变 (Immutable): Record 的所有字段默认都是 final 的,并且没有 setter 方法。这使得它天然线程安全,非常适合作为 DTO (数据传输对象) 或值对象 (Value Object)。

  3. 3. 清晰 (Clear): 它清晰地表达了“我就是一个数据容器”的语义,而不是一个包含复杂行为的对象。

Record vs. Lombok:一场“新王”与“旧王”的对决

提到消除样板代码,很多开发者会立刻想到一个功勋卓著的“旧王”—— Lombok。通过 @Data@Value@Builder 等注解,Lombok 也能实现类似的效果。

那么在 2025 年的今天,我们该如何选择?

Lombok 的实现:

import lombok.Data;

@Data // @Data = @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor
public class User {
    private Long id;
    private String username;
    private String email;
}

对比分析:

特性

Java Record

Lombok (@Data / @Value)

性质 原生语言特性 第三方库 (注解处理器)
不可变性 默认不可变 (Immutable)

可选 (@Value 不可变, @Data 可变)

简洁性

极致简洁(一行)

高度简洁(几个注解)

访问器命名 user.id()

 (无 get 前缀)

user.getId()

 (标准 JavaBean 规范)

环境依赖 无需任何配置

,现代IDE和构建工具原生支持

需要在 IDE 中安装插件,并在构建工具中配置注解处理器

代码“魔法”程度

低,所见即所得,代码是透明的

高,“魔法”般地在编译期生成代码,源码中不可见

灵活性

较低,专为数据载体设计

极高,可通过不同注解自由组合 gettersetterbuilder 等

选择建议:

  • • 优先选择 Record 当你需要定义一个简单的、不可变的数据载体时(例如 DTOs, API 响应, 配置类),Record 应该是你的首选。它是 Java 官方的、最符合语言发展方向的解决方案。

  • • 在需要时使用 Lombok 当你需要更强的灵活性时,Lombok 依然是强大的补充。例如:

    • • 你需要一个可变的对象(需要 setter)。

    • • 你需要使用建造者模式 (@Builder)

    • • 你需要一个无参构造函数Record 默认没有)。

    • • 你的项目因为历史原因,要求必须遵循 JavaBean 的 getXxx() 命名规范。

“未来时”:Record 与模式匹配的完美结合

Record 的美妙之处不止于简洁。它与 Java 21 之后全面落地的模式匹配 (Pattern Matching) 结合使用时,能产生惊人的化学反应。

1. 模式匹配 for instanceof

旧方式:

if (obj instanceof User) {
    User user = (User) obj;
    System.out.println("Username is: " + user.getUsername());
}

新方式 (结合 Record):

// 如果 obj 是 User 类型,则自动将其解构为 id, username, email 变量
if (obj instanceof User(Long id, String username, String email)) {
    System.out.println("Username is: " + username); // 直接使用解构后的变量
}

代码更安全(无需强转),也更简洁。

2. 模式匹配 for switch

这是最强大的组合。它可以写出极其优雅、类型安全的多态处理代码。

场景: 处理一个可能是 Customer 或 Employee 的 Person

// 使用 sealed interface 限制 Person 的子类型
sealed interface Person permits Customer, Employee {}
record Customer(String name, String level) implements Person {}
record Employee(String name, String title) implements Person {}

// switch 可以直接对类型进行匹配和解构
public String greet(Person person) {
    return switch (person) {
        case Customer(var name, var level) -> "Hello customer " + name + " (" + level + ")";
        case Employee(var name, var title) -> "Hello employee " + name + " (" + title + ")";
    };
}

这段代码不仅完全消除了 if-else 和 instanceof,而且由于 Person 是一个 sealed 接口,编译器还会检查 switch 是否穷尽了所有可能的子类型,从根本上杜绝了漏掉某个 case 的风险。

结论

Java 确实在不断地进化,努力让自己变得更简洁、更安全、更具表达力。Record 和模式匹配就是近年来 Java 在“语法糖”和“编程范式”上迈出的巨大一步。

更多推荐