前言
在 Java 开发的征途中,我们时常与重复代码不期而遇。这些重复代码不仅让项目显得笨重,更增加了维护成本。幸运的是,Java 8 带来了函数式编程的春风,以 Function 接口为代表的一系列新特性,为我们提供了破除这一难题的利剑。本文将以一个实际应用场景为例,即使用 Java 8 的函数式编程特性来重构数据有效性断言逻辑,展示如何通过 SFunction(基于 Java 8 的 Lambda 表达式封装)减少代码重复,从而提升代码的优雅性和可维护性。
背景故事
数据校验的烦恼想象一下,在一个复杂的业务系统中,我们可能需要频繁地验证数据库中某个字段值是否有效,是否符合预期值。传统的做法可能充斥着大量相似的查询逻辑,每次都需要手动构建查询条件、执行查询并处理结果,这样的代码既冗长又难以维护。例如以下两个验证用户 ID 和部门 ID 是否有效的方法,虽然简单,但每次需要校验不同实体或不同条件时,就需要复制粘贴并做相应修改,导致代码库中充满了大量雷同的校验逻辑,给维护带来了困扰。
// 判断用户 ID 是否有效 public void checkUserExistence(String userId) { User user = userDao.findById(userId); if (user == null) { throw new RuntimeException("用户ID无效"); } }// 判断部门 ID 是否有效 public void checkDeptExistence(String deptId) { Dept dept = deptDao.findById(deptId); if (dept == null) { throw new RuntimeException("部门ID无效"); } }
Java 8 的魔法棒
函数式接口Java 8 引入了函数式接口的概念,其中 Function<T, R> 是最基础的代表,它接受一个类型 T 的输入,返回类型 R 的结果。而在 MyBatis Plus 等框架中常用的 SFunction 是对 Lambda 表达式的进一步封装,使得我们可以更加灵活地操作实体类的属性。
实战演练
重构断言方法下面的 ensureColumnValueValid 方法正是利用了函数式接口的魅力,实现了对任意实体类指定列值的有效性断言:
/** * 确认数据库字段值有效(通用) * * @param <V> 待验证值的类型 * @param valueToCheck 待验证的值 * @param columnExtractor 实体类属性提取函数 * @param queryExecutor 单条数据查询执行器 * @param errorMessage 异常提示信息模板 */ public static <T, R, V> void ensureColumnValueValid(V valueToCheck, SFunction<T, R> columnExtractor, SFunction<LambdaQueryWrapper<T>, T> queryExecutor, String errorMessage) { if (valueToCheck == null) return; LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>(); wrapper.select(columnExtractor); wrapper.eq(columnExtractor, valueToCheck); wrapper.last("LIMIT 1"); T entity = queryExecutor.apply(wrapper); R columnValue = columnExtractor.apply(entity); if (entity == null || columnValue == null) throw new DataValidationException(String.format(errorMessage, valueToCheck)); }
参考博客:利用 Function 接口告别冗余(屎山)代码