恋上蓝花楹

Java 17 新特性实战:Record、Sealed Classes 与 Pattern Matching 让代码更优雅

Java 17 是继 Java 11 之后的第二个 LTS(长期支持)版本,于 2021 年 9 月正式发布。对于还在用 Java 8 或 Java 11 的开发者来说,升级到 Java 17 能获得大量语法糖和性能提升。本文结合实际项目场景,带你快速掌握 Java 17 最值得用的几个新特性。

1. Record 类:告别冗余的 POJO

在 Java 17 之前,写一个数据传输对象(DTO)需要大量样板代码:构造函数、getter、equals、hashCode、toString……即使借助 Lombok,也要加一堆注解。Record 类彻底解决了这个问题。

// 传统写法(加上 Lombok 也要这些注解)
@Data
@AllArgsConstructor
public class UserDTO {
    private Long id;
    private String name;
    private String email;
}

// Java 17 Record 写法
public record UserDTO(Long id, String name, String email) {}

一行代码搞定!Record 自动生成:所有字段的访问方法(注意是 id() 而非 getId())、equals()hashCode()toString(),以及全参构造函数。

Record 是不可变的,字段默认 final,非常适合用作 API 响应体、事件对象、值对象等场景。在 Spring Boot 项目中,配合 Jackson 使用时记得确保 Jackson 版本支持 Record(2.12+ 即可)。

// 自定义校验逻辑也很简单
public record UserDTO(Long id, String name, String email) {
    public UserDTO {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("name 不能为空");
        }
    }
}

2. Sealed Classes:精确控制继承体系

Sealed 类允许你明确声明哪些类可以继承它,非常适合用来建模有限的业务状态。

// 定义支付结果只有三种可能
public sealed interface PaymentResult
    permits PaymentSuccess, PaymentFailed, PaymentPending {}

public record PaymentSuccess(String transactionId, BigDecimal amount) implements PaymentResult {}
public record PaymentFailed(String errorCode, String message) implements PaymentResult {}
public record PaymentPending(String orderId) implements PaymentResult {}

这样做的好处是:IDE 和编译器都知道 PaymentResult 只有这三种实现,在 switch 表达式中可以做完整性检查,不会漏掉某个分支。

3. Pattern Matching for instanceof:消灭强制转型

以前写类型判断代码,总要先 instanceof 再强转,Java 17 把这两步合并了:

// 旧写法
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toUpperCase());
}

// Java 17 新写法
if (obj instanceof String s) {
    System.out.println(s.toUpperCase());
}

结合 Sealed Classes,switch 表达式变得极其优雅:

String describe(PaymentResult result) {
    return switch (result) {
        case PaymentSuccess s -> "支付成功,交易号:" + s.transactionId();
        case PaymentFailed f -> "支付失败:" + f.message();
        case PaymentPending p -> "处理中,订单号:" + p.orderId();
    };
}

编译器会检查是否覆盖了所有子类,漏掉任何一个都会报错,彻底避免了遗漏分支的 bug。

4. Text Blocks:多行字符串不再痛苦

写 SQL、JSON、HTML 模板时,以前要拼接字符串或者用 StringBuilder,现在用 Text Blocks 清爽多了:

// 旧写法
String sql = "SELECT u.id, u.name, o.amount\n" +
             "FROM users u\n" +
             "JOIN orders o ON u.id = o.user_id\n" +
             "WHERE u.status = 'active'\n" +
             "ORDER BY o.created_at DESC";

// Text Blocks 写法
String sql = """
        SELECT u.id, u.name, o.amount
        FROM users u
        JOIN orders o ON u.id = o.user_id
        WHERE u.status = 'active'
        ORDER BY o.created_at DESC
        """;

在 Spring Boot 项目里,写原生 SQL 查询、构造 JSON 请求体、定义邮件模板时,Text Blocks 能大幅提升代码可读性。

5. 实际迁移建议

如果你的项目还在用 Java 8 或 Java 11,升级到 Java 17 的路径其实比想象中平滑:

  • Spring Boot 3.x 强制要求 Java 17+,如果你计划升级 Spring Boot,Java 17 是必经之路
  • 先升级 JDK,不改代码,跑一遍测试,大多数项目能直接通过
  • 逐步用 Record 替换纯数据类,用 Text Blocks 替换多行字符串拼接
  • 新功能(Sealed Classes + Pattern Matching)在新模块中引入,不必一次性重构

小结

Java 17 的这些特性不是花哨的语法糖,而是真正解决了日常开发中的痛点:Record 减少样板代码,Sealed Classes 让业务建模更严谨,Pattern Matching 让类型处理更安全,Text Blocks 让多行字符串可读。

对于正在用 Spring Boot 3.x 的项目来说,这些特性已经是标配。如果你还没用上,不妨从下一个新功能模块开始尝试,感受一下现代 Java 的开发体验。

wulilele

我是一名热爱科技与AI的软件工程师。