还在手写 getter/setter?你可能错过了 Java 近年来最棒的语法糖

作为一个 Java 开发者,请闭上眼睛,回想一下你曾写过的那些 User.java, OrderDTO.java, ProductVO.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)。这些 getter, setter, equals, hashCode, toString 方法,几乎占据了数据对象 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. 简洁 (Concise): 极大地减少了样板代码,让类的核心意图(承载数据)一目了然。
-
2. 不可变 (Immutable):
Record的所有字段默认都是final的,并且没有setter方法。这使得它天然线程安全,非常适合作为 DTO (数据传输对象) 或值对象 (Value Object)。 -
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) |
可选 ( |
| 简洁性 |
极致简洁(一行) |
高度简洁(几个注解) |
| 访问器命名 | user.id()
(无 |
user.getId()
(标准 JavaBean 规范) |
| 环境依赖 | 无需任何配置
,现代IDE和构建工具原生支持 |
需要在 IDE 中安装插件,并在构建工具中配置注解处理器 |
| 代码“魔法”程度 |
低,所见即所得,代码是透明的 |
高,“魔法”般地在编译期生成代码,源码中不可见 |
| 灵活性 |
较低,专为数据载体设计 |
极高,可通过不同注解自由组合 |
选择建议:
-
• 优先选择
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 在“语法糖”和“编程范式”上迈出的巨大一步。
更多推荐

所有评论(0)