
























Java Record 是从 Java 14 开始预览,并在 Java 16 正式发布的特性。它是一种专门用于承载不可变数据的轻量级类,可以看作是一种“具名元组”。它的核心使命是简化数据载体类(Data Carrier)的编写,让你告别为 POJO 重复编写构造器、Getter、equals、hashCode 和 toString 等样板代码的痛苦。
简单来说,以前写一个只有两个字段的类需要几十行代码,用 Record 只需一行。
定义一个 Record 使用 record 关键字:
public record Person(String name, int age) {}
仅此一行,编译器会自动为你生成以下所有内容:
private final 字段,确保不可变性。name()、age(),没有 get 前缀。equals() 与 hashCode():基于所有组件(字段)值的实现。toString():格式为 Person[name=..., age=...] 的字符串。它的使用方式和普通类几乎一样,但更具声明性:
Person person = new Person("Alice", 30);
String name = person.name(); // 调用自动生成的Getter
System.out.println(person); // Person[name=Alice, age=30]
Record 并非完全死板,它允许在紧凑语法上进行有限的定制。
1. 自定义构造器(紧凑构造器)
你可以添加验证逻辑,不需要指定参数列表,也不能给字段赋值(编译器会自动完成):
public record Person(String name, int age) {
// 这就是紧凑构造器,可以加逻辑
public Person {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
// 无需写 this.name = name,编译器会自动生成在末尾
}
}
2. 添加实例方法或静态方法
public record Person(String name, int age) {
public String nameToUpperCase() {
return name.toUpperCase();
}
public static Person fromAnonymous() {
return new Person("Anonymous", 0);
}
}
3. 实现接口
Record 可以自由地实现接口,参与多态。
public interface Greetable { String greet(); }
public record Person(String name, int age) implements Greetable {
@Override
public String greet() { return "Hi, I'm " + name; }
}
为了实现“纯粹的数据载体”目标,Record 有几条硬性限制,以保证语义的纯粹性:
final 的,且无法继承其他类。因为它已隐式继承 java.lang.Record 父类。final:保证实例一旦创建就不可变。当你需要创建如下类型的对象时,Record 是完美的选择:
equals 和 hashCode。| 特性 | Java Record | 传统 POJO | Lombok @Value |
|---|---|---|---|
| 语法简洁度 | 极高,一行声明 | 极低,需 IDE 生成或手写 | 高,依赖注解处理 |
| 不可变性 | 语言级保证 | 需手动实现 | 注解级保证 |
| 本质 | 一种新的类类型 | 普通类 | 编译期代码增强 |
| 继承限制 | final 类,仅能实现接口 |
无限制 | 无限制 |
import java.util.HashMap;
import java.util.Map;
public class RecordDemo {
public static void main(String[] args) {
// 定义坐标记录
record Coordinate(int x, int y) {}
var map = new HashMap<Coordinate, String>();
map.put(new Coordinate(1, 2), "宝藏");
// 因为equals/hashCode基于x,y的值,所以能正确取出
System.out.println(map.get(new Coordinate(1, 2))); // 输出:宝藏
}
}
总的来说,Java Record 让你在处理纯数据时,代码更简洁、意图更清晰、不可变性更安全。它很适合用来替代那些只需要承载数据的传统类。
Record 本身设计为最终类,如果你需要为基于 Record 的模型提供序列化支持,想了解如何与 Jackson 等 JSON 库搭配使用吗?
当需要将 Java Record 序列化成 JSON 或将 JSON 反序列化为 Record 时,Jackson 提供了很好的支持。下面介绍几种常用方式。
在 pom.xml 中添加 Jackson 依赖(以 2.15 为例):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
从 Jackson 2.12 开始,对 Record 的支持是内置且无需额外注解的,可以直接序列化和反序列化。
1. Record 定义
public record Person(String name, int age, String email) {}
2. 序列化与反序列化
import com.fasterxml.jackson.databind.ObjectMapper;
public class RecordJsonDemo {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
// Java Record -> JSON 字符串
Person person = new Person("张三", 28, "zhangsan@example.com");
String json = mapper.writeValueAsString(person);
System.out.println("JSON: " + json);
// 输出: JSON: {"name":"张三","age":28,"email":"zhangsan@example.com"}
// JSON 字符串 -> Java Record
String jsonStr = "{\"name\":\"李四\",\"age\":35,\"email\":\"lisi@example.com\"}";
Person deserializedPerson = mapper.readValue(jsonStr, Person.class);
System.out.println("反序列化: " + deserializedPerson);
// 输出: 反序列化: Person[name=李四, age=35, email=lisi@example.com]
}
}
@JsonProperty若 JSON 字段名与 Record 组件名不一致,使用 @JsonProperty:
public record Person(
@JsonProperty("full_name") String name,
@JsonProperty("years_old") int age,
String email
) {}
对于需要验证或转换的场景,可以在紧凑构造器中处理:
import com.fasterxml.jackson.annotation.JsonCreator;
public record Person(String name, int age, String email) {
@JsonCreator // 明确指示 Jackson 使用此构造器
public Person {
if (age < 0) throw new IllegalArgumentException("年龄非法");
// 可以做一些规范化,比如 trim
}
}
使用 Optional 需特别注意:Jackson 默认能处理 Optional 类型,但更推荐用 @JsonInclude 控制非空序列化,或在反序列化时给默认值。
为字段设置默认值(反序列化时若 JSON 缺失该字段):
public record Person(String name, int age, String email) {
// 手工重载规范构造器,在内部提供默认值
@JsonCreator
public Person(@JsonProperty("name") String name,
@JsonProperty("age") int age,
@JsonProperty("email") String email) {
this.name = name == null ? "未知" : name;
this.age = age;
this.email = email == null ? "no-email@example.com" : email;
}
}
static record),否则 Jackson 可能无法实例化。假设一个 API 返回用户信息,Record 作为 DTO:
// 定义 Record
public record ApiResponse<T>(int code, String message, T data) {}
// 在 Controller 中使用 (Spring Boot 示例)
@GetMapping("/user/{id}")
public ApiResponse<Person> getUser(@PathVariable int id) {
Person user = new Person("王五", 30, "wangwu@example.com");
return new ApiResponse<>(200, "成功", user);
}
// 返回的 JSON 自动为: {"code":200,"message":"成功","data":{"name":"王五","age":30,"email":"wangwu@example.com"}}
Jackson 从 2.12 起对 Record 的自动化支持已经相当成熟,无需额外插件或复杂配置即可无缝集成到 Spring Boot 等框架中使用。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。