Java14到16语法简化篇-instanceof模式匹配-文本块与Record

4 阅读3分钟

前言

"很多人升级 JDK 后,第一反应不是性能变好了,而是代码终于没那么啰嗦了。"

Java 14 到 Java 16 这一段演进,最直接的价值就是持续减少样板代码。

这篇文章聚焦 3 个非常有代表性的能力:instanceof 模式匹配、文本块、Record。它们不一定改变你的系统架构,但会持续改善你每天写代码的手感。


一、instanceof 模式匹配:把判断和转换合成一步

先说一个容易写错的版本点:

  • JDK 14:预览特性
  • JDK 15:第二次预览
  • JDK 16:正式定稿

所以更准确的表述不是“Java 14 引入后就能稳定使用”,而是“从 JDK 14 开始预览,到 JDK 16 正式可用”。

传统写法:

if (obj instanceof String) {
    String str = (String) obj;
    System.out.println(str.length());
}

模式匹配写法:

if (obj instanceof String str) {
    System.out.println(str.length());
}

实战里非常适合这种场景:

if (request instanceof ContentCachingRequestWrapper wrapper) {
    byte[] content = wrapper.getContentAsByteArray();
}

Object score = response.get("score");
if (score instanceof Number number) {
    return number.doubleValue();
}

二、文本块:多行字符串终于能好好写了

文本块在 Java 15 正式定稿,用三个双引号 """ 表示多行字符串。

传统写法:

String json = "{\n" +
    "  \"name\": \"张三\",\n" +
    "  \"age\": 25\n" +
    "}";

文本块写法:

String json = """
    {
      "name": "张三",
      "age": 25
    }
    """;

最适合哪些场景

  1. SQL
  2. JSON
  3. HTML 模板
  4. 测试请求体

例如 MyBatis 注解写 SQL:

@Select("""
    SELECT id, username, email, status
    FROM sys_user
    WHERE status = #{status}
    ORDER BY created_at DESC
    """)
List<SysUserEntity> selectByStatus(@Param("status") String status);

动态 SQL 也能配合 <script>

@Select("""
    <script>
    SELECT * FROM sys_user
    WHERE deleted = false
    <if test="status != null">
        AND status = #{status}
    </if>
    <if test="keyword != null and keyword != ''">
        AND (username LIKE CONCAT('%', #{keyword}, '%')
             OR email LIKE CONCAT('%', #{keyword}, '%'))
    </if>
    </script>
    """)
List<SysUserEntity> selectByCondition(@Param("status") String status,
                                      @Param("keyword") String keyword);

测试代码里也很舒服:

mockMvc.perform(post("/api/admin/auth/login")
    .contentType(MediaType.APPLICATION_JSON)
    .content("""
        {
          "loginId": "admin",
          "password": "Admin@123456"
        }
        """));

📌 注意点

  • 文本块不支持变量插值,但可以配合 formatted()
  • 缩进规则要理解清楚,必要时用 stripIndent()
  • 行尾的 \ 可以消除换行

三、Record:DTO 样板代码的终结者

Record 在 Java 16 正式定稿,用来定义“不可变数据载体”非常合适。

传统 DTO:

public class UserDTO {
    private final Long id;
    private final String username;
    private final String email;

    public UserDTO(Long id, String username, String email) {
        this.id = id;
        this.username = username;
        this.email = email;
    }

    public Long getId() { return id; }
    public String getUsername() { return username; }
    public String getEmail() { return email; }
}

Record 写法:

public record UserDTO(Long id, String username, String email) {}

很适合的场景

public record ModelResponse(
    Long id,
    Long providerId,
    String providerCode,
    String providerName,
    String modelCode,
    String modelName,
    List<String> capabilityTypes,
    String modelType,
    Integer maxTokens,
    BigDecimal temperatureDefault,
    String status
) {}

还可以加注解、方法、紧凑构造器:

private record BailianChatUsage(
    @JsonProperty("prompt_tokens") Integer promptTokens,
    @JsonProperty("completion_tokens") Integer completionTokens
) {}

public record RetrievedChunk(ChunkEntity chunk, double score) {
    public RetrievedChunk {
        if (score < 0 || score > 1) {
            throw new IllegalArgumentException("score must be between 0 and 1");
        }
    }
}

Record 和 Lombok 怎么选

场景推荐
API 响应 DTORecord
内部只读数据载体Record
JPA 实体类Lombok / 普通类
需要可变对象Lombok @Data 或普通类
需要继承普通类或 Lombok

📌 注意点

  • Record 适合“数据载体”,不适合承担复杂可变业务行为
  • Record 不能继承普通类,但可以实现接口
  • Record 天生更适合不可变建模

总结

Java 14 到 16 这段演进带来的最直观收益,就是“少写废话”:

  1. instanceof 模式匹配减少强转样板代码
  2. 文本块让多行字符串终于可读
  3. Record 让 DTO 回归数据本身

如果你已经在 Java 17+ 或 Java 21 上开发,这些能力完全应该成为默认写法的一部分。


欢迎关注公众号 FishTech Notes,一块交流使用心得!