欢迎光临

Java Record与Sealed Class深度实战:从数据建模到模式匹配的现代化Java编程

Java语言在过去几年经历了翻天覆地的变化。从Java 14引入Records预览版,到Java 17正式发布Sealed Classes,再到Java 21的Pattern Matching for switch,一系列新特性正在彻底改变Java开发者编写数据驱动代码的方式。本文将深入探讨Record与Sealed Class这两个核心特性,从语法细节到实战应用,帮助你写出更简洁、更安全、更具表达力的Java代码。

Java代码编程

一、Record:不可变数据载体的终极方案

在Java 14之前,我们要创建一个简单的数据载体类,需要编写大量样板代码:私有字段、构造器、getter、equals、hashCode、toString。即使使用IDE自动生成,代码依然冗长且容易出错——修改字段后忘记更新equals/hashCode是常见的Bug来源。

Record类型正是为了解决这个问题而诞生的。它是一种特殊形式的类,用来表示不可变数据。编译器会自动生成所有必要的方法,让你专注于数据本身。

1.1 Record基础语法

一个Record的声明极其简洁:


1
public record Point(int x, int y) {}

这一行代码等价于一个包含以下内容的传统类:

  • 两个private final字段
    1
    x

    1
    y
  • 一个接收所有字段的规范构造器(canonical constructor)
  • 字段的访问器方法(
    1
    x()

    1
    y()

    ,注意不是

    1
    getX()

  • 基于所有字段的
    1
    equals()

    1
    hashCode()

    1
    toString()

    实现

让我们看一个更实际的例子——HTTP请求响应模型:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public record ApiResponse<T>(
    int statusCode,
    String message,
    T data,
    LocalDateTime timestamp
) {
    // 自定义紧凑构造器,添加验证逻辑
    public ApiResponse {
        if (statusCode < 100 || statusCode > 599) {
            throw new IllegalArgumentException("Invalid HTTP status code: " + statusCode);
        }
        if (timestamp == null) {
            timestamp = LocalDateTime.now();
        }
    }

    // 便捷工厂方法
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200, "OK", data, LocalDateTime.now());
    }

    public static <T> ApiResponse<T> error(int code, String message) {
        return new ApiResponse<>(code, message, null, LocalDateTime.now());
    }
}

1.2 紧凑构造器与规范构造器

Record支持两种构造器形式。紧凑构造器(Compact Constructor)没有参数列表,编译器会自动将参数匹配到对应字段。它非常适合做参数验证和默认值设置:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public record User(String name, String email, int age) {

    // 紧凑构造器 —— 参数列表省略,但可以修改值
    public User {
        Objects.requireNonNull(name, "Name cannot be null");
        Objects.requireNonNull(email, "Email cannot be null");
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("Invalid age: " + age);
        }
        // 规范化数据
        name = name.strip();
        email = email.strip().toLowerCase();
    }

    // 自定义非规范构造器,必须委托给规范构造器
    public User(String name, String email) {
        this(name, email, 0);
    }
}

关键要点:在紧凑构造器中赋值会在字段初始化之前执行,你可以修改参数的值,这些修改后的值会被用于字段初始化。这是与普通构造器的重要区别。

1.3 Record的实现细节与限制

Record虽然在语法上简洁,但它有明确的限制以保持其语义纯粹性:

特性 Record 普通类
隐式继承 java.lang.Record java.lang.Object
声明字段 ❌ 只能有组件字段 ✅ 任意字段
声明实例字段 ❌ 禁止 ✅ 允许
声明静态字段 ✅ 允许 ✅ 允许
实现接口 ✅ 允许 ✅ 允许
继承其他类 ❌ 禁止 ✅ 允许
被继承 ❌ final 可配置
声明本地方法 ❌ 禁止 ✅ 允许
字段可变性 不可变(final) 可配置

虽然不能声明实例字段,但你可以声明静态字段和静态方法,以及实例方法。这为工具方法提供了空间:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public record Money(BigDecimal amount, Currency currency) {

    // 静态字段
    public static final Money ZERO_YUAN = new Money(BigDecimal.ZERO, Currency.getInstance("CNY"));

    // 实例方法
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currency mismatch");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }

    public Money multiply(int factor) {
        return new Money(this.amount.multiply(BigDecimal.valueOf(factor)), this.currency);
    }
}

软件开发

二、Sealed Class:受控继承的类型安全利器

在Sealed Class出现之前,Java的类型继承体系要么完全开放(任何类都能继承),要么完全封闭(final类不允许继承)。Sealed Class填补了中间地带——允许继承,但只允许特定的类继承。

这对于领域建模至关重要。在函数式编程中,代数数据类型(ADT)是核心概念,Sealed Class让Java也能优雅地表达ADT。

2.1 Sealed Class基础

Sealed Class使用

1
sealed

修饰符声明,并通过

1
permits

子句指定允许的子类:


1
2
3
4
5
6
public sealed interface Shape
    permits Circle, Rectangle, Triangle {}

public record Circle(double radius) implements Shape {}
public record Rectangle(double width, double height) implements Shape {}
public record Triangle(double a, double b, double c) implements Shape {}

子类必须是以下三种之一:

  • final —— 不可再被继承(record隐式为final)
  • sealed —— 可以被继承,但必须声明自己的permits列表
  • non-sealed —— 开放继承,任何人都能继承(回到传统模式)

这种设计确保了类型继承的层次结构在编译期就是已知的,这是Pattern Matching能安全工作的前提。

2.2 实战:支付系统领域建模

让我们用一个真实的支付系统来展示Sealed Class的威力。一个支付系统需要支持多种支付方式,每种方式有不同的验证逻辑和手续费计算方式:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public sealed interface PaymentMethod permits CreditCard, WeChatPay, Alipay, BankTransfer {

    /**
     * 验证支付方式是否有效
     */
    boolean isValid();

    /**
     * 计算手续费
     */
    BigDecimal calculateFee(BigDecimal amount);
}

public record CreditCard(
    String cardNumber,
    String holderName,
    LocalDate expiryDate,
    String cvv
) implements PaymentMethod {

    @Override
    public boolean isValid() {
        return cardNumber != null && cardNumber.length() == 16
            && expiryDate.isAfter(LocalDate.now());
    }

    @Override
    public BigDecimal calculateFee(BigDecimal amount) {
        // 信用卡手续费:1.5% + 固定0.3元
        return amount.multiply(BigDecimal.valueOf("0.015"))
            .add(BigDecimal.valueOf("0.3"));
    }
}

public record WeChatPay(
    String openId,
    boolean isVerified
) implements PaymentMethod {

    @Override
    public boolean isValid() {
        return openId != null && isVerified;
    }

    @Override
    public BigDecimal calculateFee(BigDecimal amount) {
        // 微信支付:0.6%
        return amount.multiply(BigDecimal.valueOf("0.006"));
    }
}

public record Alipay(
    String userId,
    String accountType
) implements PaymentMethod {

    @Override
    public boolean isValid() {
        return userId != null
            && Set.of("personal", "enterprise").contains(accountType);
    }

    @Override
    public BigDecimal calculateFee(BigDecimal amount) {
        // 支付宝:0.55%
        return amount.multiply(BigDecimal.valueOf("0.0055"));
    }
}

public record BankTransfer(
    String bankCode,
    String accountNumber,
    String accountName
) implements PaymentMethod {

    @Override
    public boolean isValid() {
        return bankCode != null && accountNumber != null;
    }

    @Override
    public BigDecimal calculateFee(BigDecimal amount) {
        // 银行转账:固定5元
        return BigDecimal.valueOf("5.0");
    }
}

2.3 Sealed Class的包组织策略

当所有子类都在同一个源文件中时,

1
permits

子句可以省略。但在实际项目中,通常需要分开文件。这时需要注意几个规则:

  • 子类必须与sealed类在同一个模块中(未命名模块则在同一个包中)
  • 子类必须在声明中直接声明自己是sealed类的子类
  • 子类必须确实是sealed类的子类型(不能只是声明而不实现)

推荐的项目结构:


1
2
3
4
5
6
com.example.payment/
├── PaymentMethod.java          // sealed interface
├── CreditCard.java             // record implements PaymentMethod
├── WeChatPay.java              // record implements PaymentMethod
├── Alipay.java                 // record implements PaymentMethod
└── BankTransfer.java           // record implements PaymentMethod

代码架构

三、Pattern Matching for switch:类型安全的模式匹配

Sealed Class真正发挥威力,是在与Pattern Matching for switch配合使用时。Java 21正式引入的switch模式匹配,让你能用类型模式(type pattern)精确匹配不同的子类型,编译器还能验证穷举性(exhaustiveness)——如果你遗漏了某个子类型,编译器会报错。

3.1 基础类型模式匹配


1
2
3
4
5
6
7
8
9
public String describePayment(PaymentMethod payment) {
    return switch (payment) {
        case CreditCard cc -> "信用卡支付 - 尾号" + cc.cardNumber().substring(12);
        case WeChatPay wp -> "微信支付 - " + (wp.isVerified() ? "已认证" : "未认证");
        case Alipay ap -> "支付宝" + ap.accountType() + "账户";
        case BankTransfer bt -> "银行转账 - " + bt.bankCode();
        // 不需要default分支!编译器知道所有子类型都已覆盖
    };
}

注意:由于

1
PaymentMethod

是sealed的,编译器知道所有可能的子类型。当你覆盖了所有permits列表中的类型时,switch表达式就是穷举的,不需要

1
default

分支。这是Sealed Class + Pattern Matching组合的核心价值——编译器帮你保证类型安全。

3.2 带守卫条件的模式匹配

Java 21引入了带守卫条件(Guarded Pattern)的when子句,让模式匹配更加强大:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public String assessRisk(PaymentMethod payment, BigDecimal amount) {
    return switch (payment) {
        case CreditCard cc when cc.expiryDate().isBefore(LocalDate.now().plusMonths(1))
            -> "高风险 - 信用卡即将过期";
        case CreditCard cc when amount.compareTo(BigDecimal.valueOf(50000)) > 0
            -> "中风险 - 大额信用卡支付";
        case CreditCard cc
            -> "低风险 - 信用卡支付";
        case WeChatPay wp when !wp.isVerified()
            -> "高风险 - 微信未认证用户";
        case WeChatPay wp when amount.compareTo(BigDecimal.valueOf(20000)) > 0
            -> "中风险 - 微信大额支付";
        case WeChatPay wp
            -> "低风险 - 微信支付";
        case Alipay ap when "enterprise".equals(ap.accountType())
            -> "低风险 - 支付宝企业账户";
        case Alipay ap
            -> "低风险 - 支付宝个人账户";
        case BankTransfer bt
            -> "低风险 - 银行转账(T+1到账)";
    };
}

3.3 嵌套模式匹配

Java 21支持嵌套模式匹配,可以在一个模式中同时解构多层对象。这在处理复杂嵌套数据时极其强大:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public sealed interface DeliveryResult {
    record Success(String trackingId, LocalDateTime estimatedDelivery) implements DeliveryResult {}
    record Failed(String reason, boolean retryable) implements DeliveryResult {}
    record Partial(String trackingId, List<String> missingItems) implements DeliveryResult {}
}

public sealed interface OrderStatus {
    record Pending(LocalDateTime createdAt) implements OrderStatus {}
    record Processing(DeliveryResult delivery) implements OrderStatus {}
    record Completed(LocalDateTime deliveredAt) implements OrderStatus {}
    record Cancelled(String reason) implements OrderStatus {}
}

// 嵌套模式匹配
public String getOrderSummary(OrderStatus status) {
    return switch (status) {
        case Pending(var time) ->
            "订单待处理 - 创建于 " + time;
        case Processing(DeliveryResult.Success(var id, var eta)) ->
            "处理中 - 已发货,快递单号:" + id + ",预计到达:" + eta;
        case Processing(DeliveryResult.Failed(var reason, var retryable)) ->
            "处理中 - 发货失败:" + reason + (retryable ? "(可重试)" : "(不可重试)");
        case Processing(DeliveryResult.Partial(var id, var items)) ->
            "处理中 - 部分发货,缺少:" + String.join("、", items);
        case Completed(var time) ->
            "已完成 - 送达时间:" + time;
        case Cancelled(var reason) ->
            "已取消 - 原因:" + reason;
    };
}

四、Record + Sealed Class + Pattern Matching综合实战

现在让我们把三个特性组合起来,构建一个完整的配置解析系统。这个系统能从不同来源加载配置,支持类型安全的配置访问和变更通知:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
 * 配置值类型 —— 使用Sealed Interface + Record定义
 */
public sealed interface ConfigValue {
    record StringValue(String value) implements ConfigValue {}
    record IntValue(int value) implements ConfigValue {}
    record DoubleValue(double value) implements ConfigValue {}
    record BoolValue(boolean value) implements ConfigValue {}
    record ListValue(List<ConfigValue> items) implements ConfigValue {}
    record MapValue(Map<String, ConfigValue> entries) implements ConfigValue {}
}

/**
 * 配置来源
 */
public sealed interface ConfigSource {
    record EnvironmentVars() implements ConfigSource {}
    record PropertiesFile(Path path) implements ConfigSource {}
    record RemoteConfig(String url, String authToken) implements ConfigSource {}
}

/**
 * 配置加载结果
 */
public sealed interface LoadResult {
    record Success(ConfigSource source, Map<String, ConfigValue> values,
                   LocalDateTime loadedAt) implements LoadResult {}
    record Failure(ConfigSource source, String error) implements LoadResult {}
    record Partial(ConfigSource source, Map<String, ConfigValue> values,
                   List<String> warnings) implements LoadResult {}
}

/**
 * 配置服务 —— 使用模式匹配实现类型安全访问
 */
public class ConfigService {

    private final Map<String, ConfigValue> config;

    public ConfigService(Map<String, ConfigValue> config) {
        this.config = Map.copyOf(config); // 防御性拷贝
    }

    /**
     * 类型安全的配置获取
     */
    public Optional<String> getString(String key) {
        return Optional.ofNullable(config.get(key))
            .filter(cv -> cv instanceof ConfigValue.StringValue)
            .map(cv -> ((ConfigValue.StringValue) cv).value());
    }

    /**
     * 使用模式匹配的通用获取方法
     */
    public Optional<Object> getValue(String key) {
        return Optional.ofNullable(config.get(key)).map(cv ->
            switch (cv) {
                case ConfigValue.StringValue(var v) -> v;
                case ConfigValue.IntValue(var v) -> v;
                case ConfigValue.DoubleValue(var v) -> v;
                case ConfigValue.BoolValue(var v) -> v;
                case ConfigValue.ListValue(var v) -> v;
                case ConfigValue.MapValue(var v) -> v;
            }
        );
    }

    /**
     * 格式化配置值(展示嵌套模式匹配)
     */
    public String formatValue(ConfigValue value) {
        return switch (value) {
            case ConfigValue.StringValue(var v) -> """ + v + """;
            case ConfigValue.IntValue(var v) -> String.valueOf(v);
            case ConfigValue.DoubleValue(var v) -> String.format("%.2f", v);
            case ConfigValue.BoolValue(var v) -> v ? "true" : "false";
            case ConfigValue.ListValue(var items) -> "[" +
                items.stream().map(this::formatValue).collect(Collectors.joining(", ")) + "]";
            case ConfigValue.MapValue(var entries) -> "{" +
                entries.entrySet().stream()
                    .map(e -> e.getKey() + ": " + formatValue(e.getValue()))
                    .collect(Collectors.joining(", ")) + "}";
        };
    }
}

技术架构设计

五、Record序列化与反序列化的最佳实践

Record与JSON框架的集成是日常开发中的高频场景。Jackson从2.12版本开始原生支持Record,但仍有一些细节需要注意:

5.1 Jackson配置


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public record UserProfile(
    String username,
    String email,
    List<String> roles,
    LocalDateTime createdAt
) {
    // Jackson需要无参构造器来反序列化?不!
    // Jackson 2.12+ 直接支持Record,但需要正确配置

    // 自定义反序列化时使用@JsonCreator
    @JsonCreator
    public UserProfile {
        // 紧凑构造器中做验证和数据清洗
        if (username == null || username.isBlank()) {
            throw new IllegalArgumentException("Username is required");
        }
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("Valid email is required");
        }
        roles = List.copyOf(roles); // 防御性拷贝确保不可变性
    }
}

// ObjectMapper配置
ObjectMapper mapper = new ObjectMapper()
    .registerModule(new JavaTimeModule())
    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
    .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);

// 使用
String json = mapper.writeValueAsString(user);
UserProfile parsed = mapper.readValue(json, UserProfile.class);

5.2 Record与JPA的集成陷阱

虽然Record作为JPA实体在技术上可行(Hibernate 6+支持),但通常不推荐。Record的设计哲学是不可变,而JPA实体的生命周期需要可变性(延迟加载、脏检查等)。更推荐的做法是:使用传统类作为JPA实体,Record作为DTO:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// JPA实体 —— 可变,适合ORM
@Entity
@Table(name = "users")
public class UserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false, unique = true)
    private String email;

    // JPA需要无参构造器
    protected UserEntity() {}

    // getters and setters...
}

// Record DTO —— 不可变,适合API传输
public record UserDto(
    Long id,
    String username,
    String email
) {
    // 从实体转换
    public static UserDto fromEntity(UserEntity entity) {
        return new UserDto(entity.getId(), entity.getUsername(), entity.getEmail());
    }
}

// 在Service层使用
@Service
public class UserService {

    private final UserRepository repository;

    public List<UserDto> findAll() {
        return repository.findAll().stream()
            .map(UserDto::fromEntity)
            .toList(); // Java 16+ toList()
    }

    public UserDto create(CreateUserRequest request) {
        UserEntity entity = new UserEntity();
        entity.setUsername(request.username());
        entity.setEmail(request.email());
        return UserDto.fromEntity(repository.save(entity));
    }
}

六、性能考量与常见误区

6.1 Record的性能特征

Record在运行时与普通类几乎没有性能差异。编译器生成的equals/hashCode方法与手写的质量相当。但有几个细节值得注意:

  • Record的toString()会输出所有字段值,对于包含大量数据或敏感信息的Record,建议重写toString()
  • equals/hashCode基于所有字段,如果你的”逻辑相等”不包含所有字段,需要自定义实现
  • Record可包含可变字段的引用——如
    1
    List

    1
    Map

    ,虽然引用不可变,但内容可变。务必在构造器中做防御性拷贝


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ⚠️ 危险:内部List可被外部修改
public record DangerousRecord(List<String> items) {}

// ✅ 安全:防御性拷贝 + 紧凑构造器
public record SafeRecord(List<String> items) {
    public SafeRecord {
        items = List.copyOf(items); // 不可变拷贝
    }
}

// 证明问题
List<String> mutableList = new ArrayList<>(List.of("a", "b"));
DangerousRecord dangerous = new DangerousRecord(mutableList);
mutableList.add("c"); // 危险!dangerous.items()现在包含"c"

List<String> anotherList = new ArrayList<>(List.of("a", "b"));
SafeRecord safe = new SafeRecord(anotherList);
anotherList.add("c"); // 安全!safe.items()仍然是["a", "b"]

6.2 Sealed Class的编译期优化

Sealed Class的permits信息在编译期就确定了,JVM可以利用这些信息进行优化。在switch模式匹配中,当编译器能验证穷举性时,不需要生成default分支的防御代码,这可以带来微小的性能提升。

更重要的是,Sealed Class让编译器在重构时提供更强的安全保障。当你添加一个新的子类型时,所有switch表达式都会报编译错误,强制你处理新的情况。这是相比于传统if-else instanceof链的巨大优势。

七、Spring Boot 3.x中的实际应用

Spring Boot 3.x全面拥抱Java 17+,Record和Sealed Class在Spring生态中有了更多用武之地:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    private final OrderService orderService;

    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    // 使用Record作为请求DTO —— 自动验证
    public record CreateOrderRequest(
        @NotBlank String productName,
        @Positive int quantity,
        @NotNull PaymentMethod paymentMethod
    ) {}

    // 使用Record作为响应DTO
    public record OrderResponse(
        Long id,
        String productName,
        BigDecimal totalAmount,
        String status
    ) {}

    @PostMapping
    public ResponseEntity<OrderResponse> create(
        @Valid @RequestBody CreateOrderRequest request
    ) {
        Order order = orderService.createOrder(
            request.productName(),
            request.quantity(),
            request.paymentMethod()
        );
        return ResponseEntity.ok(new OrderResponse(
            order.getId(),
            order.getProductName(),
            order.getTotalAmount(),
            describeStatus(order.getStatus())
        ));
    }

    // Sealed Class + Pattern Matching处理订单状态
    private String describeStatus(OrderStatus status) {
        return switch (status) {
            case OrderStatus.Pending(var time) ->
                "待处理(创建于" + time + ")";
            case OrderStatus.Processing(var delivery) ->
                "处理中 - " + formatDelivery(delivery);
            case OrderStatus.Completed(var time) ->
                "已完成(" + time + ")";
            case OrderStatus.Cancelled(var reason) ->
                "已取消(" + reason + ")";
        };
    }

    private String formatDelivery(DeliveryResult delivery) {
        return switch (delivery) {
            case DeliveryResult.Success(var id, _) -> "已发货 " + id;
            case DeliveryResult.Failed(var reason, _) -> "发货失败:" + reason;
            case DeliveryResult.Partial(_, var items) -> "部分发货";
        };
    }
}

注意上面代码中使用了Java 21的未命名变量(

1
_

)语法——当你不关心某个解构值时,可以用下划线占位。这让模式匹配代码更加简洁。

总结

Record、Sealed Class和Pattern Matching for switch三者的组合,为Java带来了函数式编程中代数数据类型的完整体验。Record负责简洁地定义不可变数据,Sealed Class负责精确控制类型层次,Pattern Matching负责优雅地处理不同类型。

三者协同工作的核心价值在于:让编译器替你保证类型安全。当你新增一个Sealed Class的子类型时,编译器会强制你在所有switch表达式中处理它;当你修改Record的字段时,equals/hashCode/toString自动更新。这种”编译即正确”的体验,是传统Java代码无法提供的。

如果你正在使用Java 17+,强烈建议在项目中逐步采用这些特性。从DTO和配置类开始使用Record,从状态机和结果类型开始使用Sealed Class,从简单的switch表达式开始使用Pattern Matching。你会发现代码变得更简洁、更安全、更具表达力——这正是现代化Java编程的魅力所在。

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » Java Record与Sealed Class深度实战:从数据建模到模式匹配的现代化Java编程
分享到: 更多 (0)