java可通过自定义assertrule等函数式接口构建轻量可复用断言机制,支持链式组合、语义化校验与业务逻辑解耦,并通过sfunction+optional提升字段断言的类型安全与错误定位精度。

Java 中可以通过自定义函数式接口,结合 Predicate、Function 或更语义化的接口,快速构建轻量、可复用、易组合的断言机制,既保留防御式编程的严谨性,又避免模板代码膨胀。关键不在于“重造轮子”,而在于用函数式抽象把校验逻辑解耦、参数化、链式化。
定义语义清晰的断言函数式接口
比起直接用 Predicate<t></t>,自定义接口能提升可读性和意图表达。例如:
AssertRule
@FunctionalInterface
public interface AssertRule<t> {
void check(T value) throws IllegalArgumentException;
default AssertRule<t> and(AssertRule<t> other) {
return t -> {
this.check(t);
other.check(t);
};
}
}</t></t></t>
相比 Predicate.test() 返回 boolean,check() 直接抛异常,语义更贴近“断言失败即中断”,也天然契合防御式编程中「非法输入立即拦截」的原则。
基于 Lambda 封装常见校验场景
把空值、范围、格式、唯一性等高频检查封装成静态工厂方法,调用时像搭积木:
-
AssertRule.notNull("用户名")→ 检查非 null -
AssertRule.lengthBetween(2, 20).on(String::length)→ 长度范围 -
AssertRule.matches("[a-zA-Z0-9_]+").on(Function.identity())→ 正则匹配 -
AssertRule.unique(user -> user.getEmail(), userService::existsByEmail)→ 远程唯一性(带副作用,需谨慎)
每个方法返回 AssertRule<t></t>,支持链式组合:
AssertRule<string> usernameRule =
AssertRule.notNull("用户名")
.and(AssertRule.lengthBetween(2, 20))
.and(AssertRule.matches("[a-zA-Z0-9_]+"));
usernameRule.check(dto.getUsername()); // 任一失败,抛带上下文的异常
</string>
与业务方法安全集成,不侵入主流程
在 service 方法入口处集中断言,不污染核心逻辑:
public User createUser(UserCreateDTO dto) {
// 一行完成多维度校验,失败自动抛出含字段名的异常
UserAsserts.validateUserCreate(dto);
return userRepository.save(
new User(dto.getUsername(), dto.getEmail())
);
}
其中 UserAsserts.validateUserCreate() 内部就是多个 AssertRule 的组合调用,甚至可从配置或注解动态加载规则,实现策略外置。
搭配 Optional 和 SFunction 增强类型安全
对实体字段做断言时,避免硬编码字符串。借助 MyBatis-Plus 的 SFunction 提取属性,再结合 Optional 处理可能为 null 的中间结果:
<t r> void assertFieldNotNull(T entity,
SFunction<t r> fieldExtractor,
String fieldName) {
R value = Optional.ofNullable(entity)
.map(fieldExtractor)
.orElse(null);
if (value == null) {
throw new IllegalArgumentException(fieldName + " 不能为空");
}
}
// 调用:assertFieldNotNull(user, User::getEmail, "邮箱");
</t></t>
这样既保持编译期类型安全,又让错误提示精准到字段,便于前端或日志定位。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











