一、核心一句话(必背)
行为参数化:把「可变的业务逻辑(一段代码 / 行为)当作参数传递进方法」,同一个方法,传入不同行为,实现不同业务,消除大量重复 if-else、重复方法,避免重复造轮子。
传统编码:参数只能传数值、对象;行为参数化:代码逻辑也能当参数,是 Lambda 核心思想。
二、为什么需要行为参数化(传统代码痛点)
场景:筛选商品,示例痛点
需求 1:筛选价格 > 100 的商品需求 2:筛选分类 = 手机的商品需求 3:筛选价格 > 50 并且分类 = 电脑
传统写法缺点:
- 每多一个筛选规则,新建一个筛选方法 → 方法泛滥、重复循环代码
- 修改筛选条件就要改方法,开闭原则差
java
// 方案差:一个条件一个方法
List<Goods> filterByPrice(List<Goods> list,Integer price){}
List<Goods> filterByCategory(List<Goods> list,String category){}
行为参数化解决方案:只写 1 个通用 filter 方法,筛选规则通过入参传入。
三、演进路线(从原始→匿名内部类→Lambda,循序渐进理解)
步骤 1:定义函数式接口(承载行为)
函数式接口:只有一个抽象方法的接口,用来封装一段行为逻辑
java
@FunctionalInterface // 标记:只能一个抽象方法
public interface GoodsPredicate {
// 参数:单个商品;返回:true/false 是否满足筛选条件
boolean test(Goods goods);
}
步骤 2:编写通用过滤方法(固定逻辑:遍历集合;变化逻辑:判断规则→接口参数)
java
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:三代调用写法对比
- 第一代:自定义实现类(笨重,新增规则新建类)
java
class PriceOver100 implements GoodsPredicate{
@Override
public boolean test(Goods g) {return g.getPrice()>100;}
}
//调用
filterGoods(list,new PriceOver100());
- 第二代:匿名内部类(省去实体类,但代码冗余)
java
filterGoods(list, new GoodsPredicate() {
@Override
public boolean test(Goods g) {
return "手机".equals(g.getCategory());
}
});
- 第三代:Lambda 表达式(行为参数化最终形态,极简)
java
// 筛选价格>100
filterGoods(list,g->g.getPrice()>100);
// 筛选分类电脑
filterGoods(list,g->"电脑".equals(g.getCategory()));
✅ 直接把判断逻辑(行为)作为第二个参数传入,不用新建类、不用匿名类
四、核心三大关键点(理解必背)
- 函数式接口是载体 :承载要传递的行为,
@FunctionalInterface注解约束 - 行为作为入参:方法形参定义为接口类型,调用时传入 Lambda(具体实现逻辑)
- 固定逻辑抽进方法,变化逻辑外部传入,一套方法适配 N 种业务
五、项目中四大落地运用场景(高频实战)
场景 1:集合筛选、排序、遍历(Stream 全套底层都是行为参数化)
java
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 传入)
java
//通用导入模板
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 传入
java
@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 传入
java
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() | 对象供给:创建对象、延迟获取数据 |
示例快速使用:
java
//Function:字符串转Integer
Function<String,Integer> func = s->Integer.parseInt(s);
Integer num = func.apply("123");
七、行为参数化优点(面试简答)
- 代码复用:固定逻辑只写一次,减少重复代码,不重复造轮子
- 扩展性极强:新增业务规则不用新增方法 / 实体类,直接传 Lambda
- 消除臃肿 if-else、过多实体类,代码简洁
- 配合 Stream,简化集合操作
八、易混点总结
- Lambda 本质:函数式接口的实例,是行为参数化的语法糖;
- 行为参数化本质:把可变算法抽离为入参,面向接口、开闭原则落地;
- 没有函数式接口,就无法实现行为参数化。
九、背诵口诀
行为参数化,逻辑当参数; 函数接口做载体,Lambda 传行为; 固定代码写内部,变化规则外部给; 筛选排序和回调,四大接口全搞定。