首页 » java

Java Record 详细用法指南

   发表于:java评论 (0)   热度:4

一、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),编译器自动生成:

  1. 私有 final 字段private final String name;

  2. 公共构造器public Person(String name, int age)

  3. 访问器方法name()age()(不是 getName()

  4. equals() 方法:基于所有字段的值相等

  5. hashCode() 方法:基于所有字段

  6. 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 限制

  1. 不能继承其他类:Record 隐式继承 java.lang.Record

  2. 字段都是 final:不能重新赋值

  3. 不能是抽象的

  4. 不能声明非静态的实例字段

  5. 不能声明 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 的场景

  1. DTO(数据传输对象)

  2. 值对象

  3. 不可变配置

  4. 方法返回多个值

  5. 临时数据载体

7.2 不适合使用 Record 的场景

  1. 需要可变状态的类

  2. 需要继承的类

  3. 复杂的业务逻辑类

  4. 实体类(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 常见面试题

  1. Record 是什么?解决了什么问题?

    • 不可变数据载体类,减少样板代码

  2. Record 和普通类有什么区别?

    • 自动生成构造器、equals、hashCode、toString

    • 字段都是 final

    • 不能继承其他类

  3. Record 能继承吗?

    • 不能继承其他类,但可以实现接口

  4. Record 的字段能修改吗?

    • 不能,所有字段都是 final

8.2 实战话术

"在我的批量任务中台中,我大量使用了 Record 来定义不可变的配置对象数据传输对象。比如 BatchTaskDefinition使用 Record 确保任务定义的不可变性,避免了运行时被意外修改的风险。同时,Record 的模式匹配特性让我在处理不同类型的任务时,代码更加简洁和安全。"

Record 是现代 Java 开发中非常重要的特性,特别适合你这种数据密集型的批量处理中台项目。合理使用 Record 可以让代码更简洁、更安全、更易维护!

 

 

(。・v・。)
喜欢这篇文章吗?欢迎分享到你的微博、QQ群,并关注我们的微博,谢谢支持。