1. 概述

在本篇文章中,我们将深入探讨在 Java 8 中如何对 Predicate 进行链式调用(chaining),以实现更灵活的数据过滤逻辑。

2. 基础示例

首先,我们来看一个简单的例子:使用单个 Predicate 来过滤一组名字列表:

@Test
public void whenFilterList_thenSuccess(){
   List<String> names = Arrays.asList("Adam", "Alexander", "John", "Tom");
   List<String> result = names.stream()
     .filter(name -> name.startsWith("A"))
     .collect(Collectors.toList());
   
   assertEquals(2, result.size());
   assertThat(result, contains("Adam","Alexander"));
}

在这个例子中,我们通过 filter() 方法和一个简单的 Predicate 表达式:

name -> name.startsWith("A")

筛选出了所有以字母“A”开头的名字。

但如果我们要应用多个条件怎么办?

3. 多个 filter 的方式

一种最直观的做法是链式调用多个 filter() 方法,每个方法对应一个条件:

@Test
public void whenFilterListWithMultipleFilters_thenSuccess(){
    List<String> result = names.stream()
      .filter(name -> name.startsWith("A"))
      .filter(name -> name.length() < 5)
      .collect(Collectors.toList());

    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

✅ 这种方式的优点是清晰易懂,缺点是可能性能略差(因为多次遍历)。

我们在这里筛选出那些以“A”开头且长度小于 5 的名字 —— 结果就是 “Adam”。

4. 复合 Predicate 方式

当然,也可以将多个条件合并成一个复合的 Predicate

@Test
public void whenFilterListWithComplexPredicate_thenSuccess(){
    List<String> result = names.stream()
      .filter(name -> name.startsWith("A") && name.length() < 5)
      .collect(Collectors.toList());

    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

这种方式更紧凑,而且可以充分利用逻辑运算符(如 &&||)来构建任意复杂的判断条件。

⚠️ 注意:这种方式虽然简洁,但可读性不如拆分多个 filter() 明显。

5. 使用 Predicate 提供的方法组合条件

Java 8 的 Predicate 接口提供了几个非常实用的默认方法:and()or()negate(),用于组合多个 Predicate 对象。

5.1. Predicate.and()

我们可以将多个 Predicate 定义出来,然后使用 and() 组合它们:

@Test
public void whenFilterListWithCombinedPredicatesUsingAnd_thenSuccess(){
    Predicate<String> predicate1 =  str -> str.startsWith("A");
    Predicate<String> predicate2 =  str -> str.length() < 5;
  
    List<String> result = names.stream()
      .filter(predicate1.and(predicate2))
      .collect(Collectors.toList());
        
    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

✅ 清晰表达了“同时满足两个条件”的语义。

5.2. Predicate.or()

同样地,我们可以使用 or() 实现“满足任一条件”的效果:

@Test
public void whenFilterListWithCombinedPredicatesUsingOr_thenSuccess(){
    Predicate<String> predicate1 =  str -> str.startsWith("J");
    Predicate<String> predicate2 =  str -> str.length() < 4;
    
    List<String> result = names.stream()
      .filter(predicate1.or(predicate2))
      .collect(Collectors.toList());
    
    assertEquals(2, result.size());
    assertThat(result, contains("John","Tom"));
}

这个例子中,“以 J 开头” 或者 “长度小于 4” 的名字都会被保留。

5.3. Predicate.negate()

如果需要取反某个条件,可以用 negate() 方法:

@Test
public void whenFilterListWithCombinedPredicatesUsingOrAndNegate_thenSuccess(){
    Predicate<String> predicate1 =  str -> str.startsWith("J");
    Predicate<String> predicate2 =  str -> str.length() < 4;
    
    List<String> result = names.stream()
      .filter(predicate1.or(predicate2.negate()))
      .collect(Collectors.toList());
    
    assertEquals(3, result.size());
    assertThat(result, contains("Adam","Alexander","John"));
}

这里我们组合了 or()negate(),表示匹配“以 J 开头”或者“长度不小于 4”的名字。

5.4. 内联组合 Predicate

你甚至不需要显式定义 Predicate 变量,可以直接在 filter() 中进行内联组合:

@Test
public void whenFilterListWithCombinedPredicatesInline_thenSuccess(){
    List<String> result = names.stream()
      .filter(((Predicate<String>)name -> name.startsWith("A"))
      .and(name -> name.length()<5))
      .collect(Collectors.toList());

    assertEquals(1, result.size());
    assertThat(result, contains("Adam"));
}

⚠️ 虽然语法上可行,但要注意加上强制类型转换 (Predicate<String>) 才能调用 and() 方法。

6. 动态组合一组 Predicate

有时候我们需要从一个集合中动态获取多个 Predicate 并统一应用,这时可以借助 Stream.reduce() 来实现。

6.1. 使用 and 合并一组 Predicate

@Test
public void whenFilterListWithCollectionOfPredicatesUsingAnd_thenSuccess(){
    List<Predicate<String>> allPredicates = new ArrayList<Predicate<String>>();
    allPredicates.add(str -> str.startsWith("A"));
    allPredicates.add(str -> str.contains("d"));        
    allPredicates.add(str -> str.length() > 4);
    
    List<String> result = names.stream()
      .filter(allPredicates.stream().reduce(x->true, Predicate::and))
      .collect(Collectors.toList());
    
    assertEquals(1, result.size());
    assertThat(result, contains("Alexander"));
}

注意这里的初始值是 x -> true,因为我们要的是所有条件都成立。

6.2. 使用 or 合并一组 Predicate

如果我们希望只要满足其中任意一个条件即可:

@Test
public void whenFilterListWithCollectionOfPredicatesUsingOr_thenSuccess(){
    List<String> result = names.stream()
      .filter(allPredicates.stream().reduce(x->false, Predicate::or))
      .collect(Collectors.toList());
    
    assertEquals(2, result.size());
    assertThat(result, contains("Adam","Alexander"));
}

此时初始值为 x -> false

7. 总结

本文介绍了 Java 8 中如何灵活地对 Predicate 进行链式操作,包括:

  • ✅ 多个 filter() 的链式调用;
  • ✅ 构造复杂逻辑表达式的 Predicate
  • ✅ 利用 and()or()negate() 组合多个 Predicate
  • ✅ 将一组 Predicate 收敛为单一条件进行过滤。

这些技巧在日常开发中非常常见,尤其是在做业务规则引擎、权限控制、数据筛选等场景时,掌握它们会让你写出更优雅的代码。

完整源码可见于 GitHub:https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-function


原始标题:Java 8 Predicate Chain