函数式接口加泛型的核心是将可变部分抽象为类型参数,不变部分保留在接口契约中;应按职责分设mapper、throwingmapper、bipredicate、consumerwithresult四类接口,泛型粒度要合理,优先复用jdk接口并补足短板,兼顾类型推断友好性。

函数式接口加泛型,核心是把“变的”抽成类型参数,“不变的”留在接口契约里。通用不等于大而全,而是能覆盖常见场景、不强求业务适配、又不牺牲类型安全。
明确职责边界:一个接口只抽象一类行为
别试图用一个 @FunctionalInterface 涵盖所有操作。比如处理数据转换、异常抛出、双参数判断、无返回执行,就该分四个接口:
-
Mapper<t r></t>:纯输入输出,R apply(T t) -
ThrowingMapper<t r e extends exception></t>:允许声明抛异常,R apply(T t) throws E -
BiPredicate<t u></t>(JDK 自带):两个输入、布尔结果,适合交集/差集判断 -
ConsumerWithResult<t r></t>:执行动作并返回结果(如 REST 调用后返回响应体),避免 void 语义在链式调用中断裂
泛型参数粒度要合理:宁可多参数,别少约束
例如分批执行工具方法,如果只写 <t> void batchExecute(List<t> items, Consumer<list>> handler)</list></t></t>,它无法区分“处理一批返回 void”和“处理一批返回 List
- 声明输入类型
T、批次元素类型E、处理结果类型R - 让 handler 是
Function<list>, R></list>,这样既支持 void(用Function<... void></...>或包装为Consumer),也支持收集结果 - 加上
int batchSize参数,而不是硬编码或依赖外部配置
兼容 JDK 与扩展性兼顾:优先复用,再封装增强
不要重复造轮子,但要补足 JDK 的短板:
- 直接用
Function<t r></t>做映射,但对可能抛检异常的场景,提供unchecked(ThrowingFunction)工具方法包装 - 用
Predicate<t></t>做过滤,但嵌套结构对比时,把“字段提取器”作为Function<a1 t></a1>和Function<a2 t></a2>两个参数传入,而不是限定必须同类型 - 对
Stream聚合类逻辑,把 key 提取、空值过滤、冲突合并三部分都设为函数参数,而不是写死在方法里
注意类型推断友好性:避免过度嵌套泛型
像 Function<list extends comparable>>, Optional>></list> 这种写法,编译器很难推断,调用方要写满类型。更实用的做法是:
- 接口定义保持简洁,如
<t extends comparable>> Optional<t> findMax(List<t> list)</t></t></t> - 工具方法用静态泛型,让调用时靠参数自动推导:
public static <k v> Map<k v> toMap(List<v> list, Function<v k> keyMapper)</v></v></k></k> - 必要时提供重载:一个带完整泛型的方法 + 一个常用类型的快捷版(如
toLongMap(...))
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











