Java Record 详细用法指南
一、Record 基础概念
record 是 Java 16 正式引入的不可变数据载体,用来替代手写的 POJO(字段 + 构造 + getter + equals + hashCode + toString)。
private record RegistryEntry(BatchTaskType type, Object executor) {}
本质等价
// 编译器自动生成的等价代码
private final class RegistryEntry {
private final BatchTaskType type;
private final Object executor;
// 全参构造
public RegistryEntry(BatchTaskType type, Object executor) {
this.type = type;
this.executor = executor;
}
// 自动生成 getter(注意命名:type() 不是 getType())
public BatchTaskType type() { return type; }
public Object executor() { return executor; }
// 自动生成 equals / hashCode / toString
}
Record 解决的问题
传统 POJO 类的痛点:
// 传统 POJO - 大量样板代码
public class Person {
private final String name;
private final int age;
public Person(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() { /* 复杂实现 */ }
}
使用 Record 简化:
// Record - 简洁明了
public record Person(String name, int age) {}
二、Record 核心语法
2.1 基本定义
// 最简单的 Record
public record User(String username, String email, LocalDate createdDate) {}
// 使用
User user = new User("alice", "alice@example.com", LocalDate.now());
System.out.println(user.username()); // 访问器方法(不是 getUsername)
System.out.println(user.email()); // 访问器方法(不是 getEmail)
2.2 Record 自动生成的内容
当你定义 record Person(String name, int age),编译器自动生成:
-
私有 final 字段:
private final String name; -
公共构造器:
public Person(String name, int age) -
访问器方法:
name()和age()(不是getName()) -
equals() 方法:基于所有字段的值相等
-
hashCode() 方法:基于所有字段
-
toString() 方法:格式为
Person[name=xxx, age=xxx]
三、Record 的高级特性
3.1 紧凑构造器(Compact Constructor)
用于参数验证和规范化:
public record Person(String name, int age) {
// 紧凑构造器 - 没有参数列表
public Person {
// 参数验证
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("姓名不能为空");
}
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄必须在0-150之间");
}
// 数据规范化
name = name.trim().toUpperCase();
}
}
3.2 定制构造器
public record Person(String name, int age) {
// 定制构造器
public Person(String name) {
this(name, 18); // 调用规范构造器
}
// 静态工厂方法
public static Person of(String name, int age) {
return new Person(name, age);
}
}
3.3 实例方法
public record Person(String name, int age) {
// 实例方法
public boolean isAdult() {
return age >= 18;
}
public String getDisplayName() {
return name + " (" + age + "岁)";
}
}
3.4 静态成员
public record Person(String name, int age) {
// 静态字段
public static final Person UNKNOWN = new Person("未知", 0);
// 静态方法
public static boolean isValidAge(int age) {
return age >= 0 && age <= 150;
}
}
四、Record 在批量任务中台中的应用
4.1 在你的项目中看到的 RegistryEntry
// 你代码中的实际例子
private record RegistryEntry(BatchTaskType type, Object executor) {}
// 使用
RegistryEntry entry = new RegistryEntry(BatchTaskType.IMPORT, importExecutor);
BatchTaskType type = entry.type(); // 获取类型
Object executor = entry.executor(); // 获取执行器
4.2 批量任务中的实际应用场景
场景1:任务定义
public record BatchTaskDefinition(
String code,
String name,
BatchTaskType type,
int chunkSize,
long timeout,
int retryCount,
String description
) {
// 紧凑构造器验证
public BatchTaskDefinition {
if (code == null || code.isBlank()) {
throw new IllegalArgumentException("任务编码不能为空");
}
if (chunkSize <= 0) {
throw new IllegalArgumentException("分片大小必须大于0");
}
if (timeout <= 0) {
throw new IllegalArgumentException("超时时间必须大于0");
}
}
// 判断是否支持重试
public boolean supportsRetry() {
return retryCount > 0;
}
}
场景2:任务执行结果
public record BatchTaskResult(
boolean success,
String message,
long startTime,
long endTime,
int totalCount,
int successCount,
int failCount,
List<String> errorMessages
) {
// 计算成功率
public double successRate() {
return totalCount > 0 ? (double) successCount / totalCount * 100 : 0;
}
// 计算执行时长
public long durationMillis() {
return endTime - startTime;
}
// 判断是否完全成功
public boolean isFullySuccessful() {
return success && failCount == 0;
}
}
场景3:分片数据
public record DataChunk<T>(
int chunkIndex,
int totalChunks,
List<T> data,
Map<String, Object> metadata
) {
// 获取分片大小
public int size() {
return data.size();
}
// 判断是否为空分片
public boolean isEmpty() {
return data.isEmpty();
}
}
五、Record 的限制和注意事项
5.1 限制
-
不能继承其他类:Record 隐式继承
java.lang.Record -
字段都是 final:不能重新赋值
-
不能是抽象的
-
不能声明非静态的实例字段
-
不能声明 native 方法
5.2 可以做什么
public record Person(String name, int age) implements Comparable<Person> {
// 可以实现接口
@Override
public int compareTo(Person other) {
return Integer.compare(this.age, other.age);
}
// 可以有静态字段和方法
private static final int ADULT_AGE = 18;
// 可以有实例方法
public boolean isAdult() {
return age >= ADULT_AGE;
}
// 可以有紧凑构造器和定制构造器
public Person {
if (age < 0) throw new IllegalArgumentException("年龄不能为负");
}
}
5.3 与 Lombok 的区别
|
特性 |
Record |
Lombok @Data |
|---|---|---|
|
不可变性 |
强制不可变 |
可选 |
|
继承 |
不能继承其他类 |
可以继承 |
|
样板代码 |
语言层面支持 |
编译时生成 |
|
序列化 |
天然支持 |
需要配置 |
|
模式匹配 |
支持 |
不支持 |
六、Record 的模式匹配(Java 16+)
6.1 instanceof 模式匹配
// 传统写法
if (obj instanceof Person) {
Person person = (Person) obj;
System.out.println(person.name());
}
// Record 模式匹配
if (obj instanceof Person person) {
System.out.println(person.name());
}
6.2 switch 表达式中的模式匹配
public String processTask(BatchTaskDefinition task) {
return switch (task.type()) {
case IMPORT -> "处理导入任务: " + task.code();
case EXPORT -> "处理导出任务: " + task.code();
case PUSH -> "处理推送任务: " + task.code();
case RECEIVE -> "处理接收任务: " + task.code();
case CLEANUP -> "处理清理任务: " + task.code();
};
}
七、最佳实践建议
7.1 适合使用 Record 的场景
-
DTO(数据传输对象)
-
值对象
-
不可变配置
-
方法返回多个值
-
临时数据载体
7.2 不适合使用 Record 的场景
-
需要可变状态的类
-
需要继承的类
-
复杂的业务逻辑类
-
实体类(Entity)
7.3 在你的批量任务中台中的建议
// ✅ 适合用 Record
public record BatchTaskConfig(String code, int chunkSize, long timeout, int retryCount) {}
public record TaskExecutionContext(String taskCode, Map<String, Object> params, Instant startTime) {}
public record ChunkResult<T>(boolean success, T data, String errorMessage) {}
// ❌ 不适合用 Record(有复杂行为)
public class BatchTaskExecutor {
// 有复杂业务逻辑,不适合用 Record
public BatchTaskResult execute(BatchTaskContext context) {
// 复杂逻辑...
}
}
八、面试要点
8.1 常见面试题
-
Record 是什么?解决了什么问题?
-
不可变数据载体类,减少样板代码
-
-
Record 和普通类有什么区别?
-
自动生成构造器、equals、hashCode、toString
-
字段都是 final
-
不能继承其他类
-
-
Record 能继承吗?
-
不能继承其他类,但可以实现接口
-
-
Record 的字段能修改吗?
-
不能,所有字段都是 final
-
8.2 实战话术
"在我的批量任务中台中,我大量使用了 Record 来定义不可变的配置对象和数据传输对象。比如
BatchTaskDefinition使用 Record 确保任务定义的不可变性,避免了运行时被意外修改的风险。同时,Record 的模式匹配特性让我在处理不同类型的任务时,代码更加简洁和安全。"
Record 是现代 Java 开发中非常重要的特性,特别适合你这种数据密集型的批量处理中台项目。合理使用 Record 可以让代码更简洁、更安全、更易维护!
(。・v・。)