一、核心一句话(必背)
行为参数化:把「可变的业务逻辑(一段代码 / 行为)当作参数传递进方法」,同一个方法,传入不同行为,实现不同业务,消除大量重复 if-else、重复方法,避免重复造轮子。
传统编码:参数只能传数值、对象;行为参数化:代码逻辑也能当参数,是 Lambda 核心思想。
二、为什么需要行为参数化(传统代码痛点)
场景:筛选商品,示例痛点
需求 1:筛选价格 > 100 的商品需求 2:筛选分类 = 手机的商品需求 3:筛选价格 > 50 并且分类 = 电脑
传统写法缺点:
- 每多一个筛选规则,新建一个筛选方法 → 方法泛滥、重复循环代码
- 修改筛选条件就要改方法,开闭原则差
// 方案差:一个条件一个方法 List<Goods> filterByPrice(List<Goods> list,Integer price){} List<Goods> filterByCategory(List<Goods> list,String category){}行为参数化解决方案:只写 1 个通用 filter 方法,筛选规则通过入参传入。
三、演进路线(从原始→匿名内部类→Lambda,循序渐进理解)
步骤 1:定义函数式接口(承载行为)
函数式接口:只有一个抽象方法的接口,用来封装一段行为逻辑
@FunctionalInterface // 标记:只能一个抽象方法 public interface GoodsPredicate { // 参数:单个商品;返回:true/false 是否满足筛选条件 boolean test(Goods goods); }步骤 2:编写通用过滤方法(固定逻辑:遍历集合;变化逻辑:判断规则→接口参数)
public static List<Goods> filterGoods(List<Goods> list, GoodsPredicate predicate){ List<Goods> res = new ArrayList<>(); for(Goods g : list){ // 调用传入的行为逻辑 if(predicate.test(g)){ res.add(g); } } return res; }固定逻辑:循环遍历封装一次;变化逻辑:外部传入,这就是行为参数化精髓
步骤 3:三代调用写法对比
- 第一代:自定义实现类(笨重,新增规则新建类)
class PriceOver100 implements GoodsPredicate{ @Override public boolean test(Goods g) {return g.getPrice()>100;} } //调用 filterGoods(list,new PriceOver100());- 第二代:匿名内部类(省去实体类,但代码冗余)
filterGoods(list, new GoodsPredicate() { @Override public boolean test(Goods g) { return "手机".equals(g.getCategory()); } });- 第三代:Lambda 表达式(行为参数化最终形态,极简)
// 筛选价格>100 filterGoods(list,g->g.getPrice()>100); // 筛选分类电脑 filterGoods(list,g->"电脑".equals(g.getCategory()));✅直接把判断逻辑(行为)作为第二个参数传入,不用新建类、不用匿名类
四、核心三大关键点(理解必背)
- 函数式接口是载体:承载要传递的行为,
@FunctionalInterface注解约束 - 行为作为入参:方法形参定义为接口类型,调用时传入 Lambda(具体实现逻辑)
- 固定逻辑抽进方法,变化逻辑外部传入,一套方法适配 N 种业务
五、项目中四大落地运用场景(高频实战)
场景 1:集合筛选、排序、遍历(Stream 全套底层都是行为参数化)
List<Goods> goodsList; // 筛选 List<Goods> phones = goodsList.stream().filter(g->"手机".equals(g.getCategory())).collect(Collectors.toList()); // 排序:排序规则作为参数 goodsList.sort((g1,g2)->g1.getPrice().compareTo(g2.getPrice())); // 遍历:消费行为传入 goodsList.forEach(g-> System.out.println(g.getName()));- filter 参数
Predicate、sort 参数Comparator、forEach 参数Consumer全是 JDK 内置函数式接口。
场景 2:自定义统一工具类(通用校验、统一处理器)
比如统一 Excel 导入:固定逻辑(读取文件、捕获异常),变化逻辑(每行数据保存逻辑通过 Lambda 传入)
//通用导入模板 public static <T> void importExcel(File file, Consumer<T> saveFunc){ //固定:解析excel List<T> data = parseExcel(file); for(T item:data){ saveFunc.accept(item); //外部传入保存逻辑 } } //使用:传入不同保存逻辑 importExcel(file,user->userService.save(user)); importExcel(file,order->orderService.save(order));场景 3:策略逻辑替换大量 if else(替代策略模式手写类)
比如优惠计算:统一结算方法,优惠策略通过 Lambda 传入
@FunctionalInterface interface Discount{ BigDecimal calc(BigDecimal money); } BigDecimal settle(BigDecimal money,Discount discount){ return discount.calc(money); } //满减 settle(new BigDecimal(200),m->m.compareTo(new BigDecimal(100))>0?m.subtract(new BigDecimal(20)):m); //折扣 settle(new BigDecimal(200),m->m.multiply(new BigDecimal("0.8")));场景 4:线程任务、异步回调
Thread(Runnable):Runnable 是函数式接口,任务行为通过 Lambda 传入
new Thread(()-> System.out.println("执行任务")).start();六、JDK 四大内置核心函数式接口(背诵,日常 90% 场景够用)
表格
| 接口 | 抽象方法 | 用途 |
|---|---|---|
| Predicate<T> | boolean test(T t) | 判断筛选:filter、条件校验 |
| Consumer<T> | void accept(T t) | 消费处理:forEach、数据保存 |
| Function<T,R> | R apply(T t) | 类型转换:map,T 转 R |
| Supplier<T> | T get() | 对象供给:创建对象、延迟获取数据 |
示例快速使用:
//Function:字符串转Integer Function<String,Integer> func = s->Integer.parseInt(s); Integer num = func.apply("123");七、行为参数化优点(面试简答)
- 代码复用:固定逻辑只写一次,减少重复代码,不重复造轮子
- 扩展性极强:新增业务规则不用新增方法 / 实体类,直接传 Lambda
- 消除臃肿 if-else、过多实体类,代码简洁
- 配合 Stream,简化集合操作
八、易混点总结
- Lambda 本质:函数式接口的实例,是行为参数化的语法糖;
- 行为参数化本质:把可变算法抽离为入参,面向接口、开闭原则落地;
- 没有函数式接口,就无法实现行为参数化。
九、背诵口诀
行为参数化,逻辑当参数;函数接口做载体,Lambda 传行为;固定代码写内部,变化规则外部给;筛选排序和回调,四大接口全搞定。