record 的校验必须在 compact constructor 中前置完成,先检查参数再调用 this(),否则字段已不可逆初始化;需覆盖 null、空白、范围越界等所有非法情况,并使用 IllegalArgumentException 等 unchecked 异常。

Record 的 canonical constructor(规范构造器)本身不提供可写入的构造体,你无法直接在里面加校验逻辑——所谓“利用它做校验”,实际是指**显式重写 compact constructor**,并在其中完成校验。这是唯一能真正阻止非法实例生成的方式。
compact constructor 必须写在 this() 调用之前
record 字段在 this() 执行后立即被 JVM 自动、不可逆地初始化为 final 值。一旦这一步完成,哪怕你紧接着抛异常,对象也已经构造完毕(哪怕没赋给变量)。所以校验只能前置:
- ✅ 正确:先检查参数合法性,再调用
this() - ❌ 错误:先
this(),再 if 判断 —— 此时字段已固化,校验纯属马后炮 - ❌ 错误:试图写
this.name = name.trim()—— 编译直接报错,record 不允许手动赋值 final 字段
校验逻辑要覆盖所有非法输入组合
record 天然不可变,没有 setter、没有构建器、没有 post-init 钩子。漏掉一种非法情况,就等于放行一个永远非法的对象:
- 空字符串、空白字符串、null 值必须显式判别,
name == null || name.trim().isEmpty()比单纯Objects.requireNonNull(name)更贴近业务语义 - 数值范围(如 age)要用
IllegalArgumentException,而不是NullPointerException—— 后者语义错位,且容易掩盖真实问题 - 若字段是
List这类可变容器,校验非空后还得做防御性拷贝:list = List.copyOf(list),否则外部仍可修改内部状态
异常类型和框架协同的关键细节
record 构造器语法禁止 throws 子句,所以必须用 unchecked exception:
- ✅ 推荐
IllegalArgumentException(参数本身不合法)或IllegalStateException(参数间逻辑冲突,如startTime > endTime) - ❌ 禁止
IOException等 checked exception —— 编译不通过 - ⚠️ 注意:JSR-303 注解(如
@NotBlank、@Min)在 record 上不会自动触发校验,JPA/Hibernate 和 Jackson 默认也不依赖它们;compact constructor 是唯一兜底入口 - ✅ Jackson 2.15.2+ 启用
RecordModule后、Hibernate 6.0+ 映射 record 实体时,都会走 compact constructor → 校验自然生效
最容易被忽略的是:compact constructor 不是“增强可选项”,而是 record 唯一合法、可靠、能真正阻断非法实例生成的校验位置。任何想绕过它、靠 getter 修正、靠外部工厂包装、靠注解驱动的想法,都会在反序列化或 ORM 映射时失效。










