160. Java Lambda 表达式 - Predicate的链式组合
在 Java 中,Predicate<T> 是一个非常实用的函数式接口,用于表示一个接收输入并返回布尔值的逻辑判断。它被广泛应用于过滤、条件判断等场景。
🎯 场景说明
假设我们有一个字符串列表,我们希望保留符合以下三个条件的字符串:
- 非
null; - 非空(即长度不为
0); - 长度小于
5个字符。
我们可以很直接地使用一个 Predicate<String> 来实现这三个判断:
Predicate<String> p = s -> (s != null) && !s.isEmpty() && s.length() < 5;
虽然这种方式功能上没问题,但代码可读性稍差,尤其是在逻辑变复杂时。
🌟 更优雅的方式:将谓词拆分 + 链式组合
我们可以将每个判断逻辑拆分成独立的 Predicate,然后使用默认方法 .and() 进行组合:
Predicate<String> nonNull = s -> s != null;
Predicate<String> nonEmpty = s -> !s.isEmpty();
Predicate<String> shorterThan5 = s -> s.length() < 5;
Predicate<String> p = nonNull.and(nonEmpty).and(shorterThan5);
👉 这样做有什么好处?
- ✅ 提高可读性:每个判断条件语义明确;
- ✅ 便于单独复用:每个小谓词都可以独立用于其他场景;
- ✅ 符合函数式编程原则:组合小函数构建复杂逻辑。
🔧 它是如何工作的?
这得益于 Predicate<T> 接口中的几个 默认方法(default methods):
| 方法 | 描述 |
|---|---|
.and(Predicate) | 与(AND)逻辑组合 |
.or(Predicate) | 或(OR)逻辑组合 |
.negate() | 非(NOT)逻辑取反 |
这些方法是 默认方法(default methods),可以直接在 Predicate 实例上链式调用,不用我们自己写组合逻辑!
🧪 示例:使用方法引用 + 默认方法提升可读性
Predicate<String> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
// 是 null 或 空
Predicate<String> isNullOrEmpty = isNull.or(isEmpty);
// 不是 null 且 非空
Predicate<String> isNotNullNorEmpty = isNullOrEmpty.negate();
// 小于 5 个字符
Predicate<String> shorterThan5 = s -> s.length() < 5;
// 最终组合
Predicate<String> p = isNotNullNorEmpty.and(shorterThan5);
✅ 解释:
Objects::isNull是静态方法引用,用于判断是否为 null;String::isEmpty是未绑定实例方法引用;or()把两个谓词组合为“是 null 或空”;negate()表示非,即“不是 null 且非空”;- 最后通过
.and()与长度判断组合形成完整判断逻辑。
💡 总结
通过使用 Predicate 接口的默认方法,我们可以:
- 🌱 构建更具表现力的判断逻辑
- 🧱 将复杂逻辑拆分为可复用的小模块
- ✨ 链式组合,提高可维护性和可读性
🧠 小练习
试试实现一个判断数字是否是 正数且为偶数 的 Predicate<Integer>,并用 .and() 和 .negate() 试试组合更多逻辑吧!
Predicate<Integer> isPositive = i -> i > 0;
Predicate<Integer> isEven = i -> i % 2 == 0;
Predicate<Integer> isPositiveAndEven = isPositive.and(isEven);