记得我刚接触Java 8时,最让我眼前一亮的不是Lambda表达式,而是这个看似简单却威力巨大的Predicate接口。当时我在处理一个用户数据过滤的需求,传统的if-else代码写了将近200行,维护起来简直是一场噩梦。直到同事推荐我用Predicate,代码量直接缩减到30行,那一刻我才真正体会到函数式编程的魅力。
Predicate本质上就是个返回布尔值的函数接口,属于java.util.function包。它的核心方法test(T t)接收一个参数,返回true或false。听起来简单对吧?但千万别小看它,就像瑞士军刀虽然小巧却能解决各种问题。举个例子,我们经常需要验证用户输入:
java复制Predicate<String> isValidEmail = email ->
email != null && email.contains("@") && email.endsWith(".com");
System.out.println(isValidEmail.test("test@example.com")); // true
这种写法比传统方法更直观,而且可以轻松组合多个条件。我在电商项目中就用它来验证商品属性:价格是否在合理范围、库存是否充足、是否参与促销活动等。当业务规则变更时,只需调整Predicate逻辑,不用重写整个验证流程。
test()是Predicate的灵魂所在,所有其他方法都是围绕它构建的。我建议初学者先用test()写出基础判断逻辑,再考虑组合操作。比如这个检查偶数的例子:
java复制Predicate<Integer> isEven = num -> num % 2 == 0;
IntStream.range(1, 10)
.forEach(i -> System.out.println(i + "是偶数? " + isEven.test(i)));
实际项目中,我常用它来做数据清洗。比如过滤掉年龄异常的用户数据:
java复制Predicate<User> isValidAge = user ->
user.getAge() > 0 && user.getAge() < 120;
users.stream().filter(isValidAge)...
and()和or()让Predicate真正强大起来。去年我做物流系统时,就用它们组合了复杂的运费计算规则:
java复制Predicate<Order> isExpress = o -> "express".equals(o.getDeliveryType());
Predicate<Order> isHeavy = o -> o.getWeight() > 10;
Predicate<Order> isFragile = o -> o.isFragile();
// 组合条件:快递且(超重或易碎)需要特殊处理
Predicate<Order> specialHandling = isExpress.and(isHeavy.or(isFragile));
注意and()是短路操作的,如果前一个条件为false,后面的就不会执行。这个特性在性能敏感的场景很有用,比如:
java复制// 先检查非空再检查长度,避免NPE
Predicate<String> isValid = s -> s != null && s.length() > 5;
以前我们写条件判断时,经常需要!"".equals(str)这种写法,现在可以用negate():
java复制Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
我在权限系统中就用它来过滤非管理员用户:
java复制Predicate<User> isAdmin = User::isAdmin;
users.stream().filter(isAdmin.negate())...
这个静态方法特别适合在Stream操作中做对象比较:
java复制Predicate<String> isDefault = Predicate.isEqual("DEFAULT");
configs.stream().filter(isDefault)...
不过要注意对象必须正确实现equals()方法,我有次就因为忘了重写equals()调试了半天。
not()是negate()的静态版本,代码更简洁:
java复制// Java 8
Predicate<Integer> isOdd = n -> n % 2 != 0;
// Java 11
Predicate<Integer> isOdd = Predicate.not(n -> n % 2 == 0);
在大项目中,我习惯把常用Predicate集中管理:
java复制public class UserPredicates {
public static Predicate<User> isActive() {
return u -> u.getStatus() == Status.ACTIVE;
}
public static Predicate<User> hasPermission(String perm) {
return u -> u.getPermissions().contains(perm);
}
}
// 使用示例
users.stream()
.filter(UserPredicates.isActive()
.and(UserPredicates.hasPermission("ADMIN")))
通过工厂方法生成动态Predicate:
java复制public static Predicate<Product> priceInRange(BigDecimal min, BigDecimal max) {
return p -> p.getPrice().compareTo(min) >= 0
&& p.getPrice().compareTo(max) <= 0;
}
// 根据用户输入动态构建
Predicate<Product> filter = priceInRange(userMin, userMax)
.and(p -> p.getCategory().equals(selectedCategory));
Predicate在Stream操作中如鱼得水。比如这个多条件分页查询:
java复制public List<Product> searchProducts(SearchCriteria criteria) {
Predicate<Product> filter = buildFilter(criteria);
return productRepo.findAll()
.stream()
.filter(filter)
.sorted(comparing(Product::getPrice))
.skip((criteria.getPage() - 1) * criteria.getSize())
.limit(criteria.getSize())
.collect(Collectors.toList());
}
空指针问题:始终把null检查放在最前面
java复制// 错误写法
Predicate<String> p = s -> s.length() > 5;
// 正确写法
Predicate<String> p = s -> s != null && s.length() > 5;
副作用问题:Predicate应该是无状态的纯函数
java复制// 错误示范(有副作用)
AtomicInteger counter = new AtomicInteger();
Predicate<String> p = s -> {
counter.incrementAndGet();
return s.length() > 5;
};
条件排序:把计算量小的条件放前面
java复制// 先检查非空再执行复杂计算
Predicate<Data> p = d -> d != null && complexCheck(d);
缓存常用Predicate:
java复制private static final Predicate<String> IS_VALID_EMAIL =
email -> email != null && email.matches(EMAIL_REGEX);
避免过度组合:太长的链式调用会影响可读性和性能
java复制// 定义各种业务规则
Predicate<Order> isHighValue = o -> o.getAmount() > 1000;
Predicate<Order> isInternational = o -> !o.getCountry().equals("CN");
Predicate<Order> isUrgent = o -> o.getPriority() == Priority.HIGH;
// 组合规则
Predicate<Order> needManualReview =
isHighValue.or(isInternational).and(isUrgent.negate());
orders.stream()
.filter(needManualReview)
.forEach(this::sendToReviewQueue);
java复制Predicate<User> isAccountActive = u -> u.getStatus() == ACTIVE;
Predicate<User> hasApiAccess = u -> u.getRoles().contains("API_USER");
Predicate<User> canAccessApi = isAccountActive.and(hasApiAccess);
public boolean checkApiAccess(User user, String apiEndpoint) {
return canAccessApi.test(user)
&& apiWhitelist.get(apiEndpoint).test(user);
}
java复制@Test
void testAgePredicate() {
Predicate<Person> isAdult = p -> p.getAge() >= 18;
assertTrue(isAdult.test(new Person(20)));
assertFalse(isAdult.test(new Person(17)));
assertThrows(NullPointerException.class,
() -> isAdult.test(null));
}
使用peek()查看中间结果:
java复制users.stream()
.filter(isActive
.and(hasPermission("EDIT"))
.and(not(isTemporary)))
.peek(u -> System.out.println("Processing: " + u))
.forEach(this::processUser);
java复制Function<String, Integer> parseLength = String::length;
Predicate<String> isLong = s -> parseLength.apply(s) > 10;
// 更简洁的写法
Predicate<String> isLong = ((Function<String, Integer>) String::length)
.andThen(len -> len > 10)::apply;
java复制Optional.ofNullable(user)
.filter(u -> u.getAge() > 18)
.ifPresent(this::processAdultUser);
java复制public class Validator {
private final Predicate<String> strategy;
public Validator(Predicate<String> strategy) {
this.strategy = strategy;
}
public boolean validate(String input) {
return strategy.test(input);
}
}
// 使用
Validator emailValidator = new Validator(
s -> s != null && s.matches(EMAIL_REGEX));
java复制public class RuleEngine<T> {
private List<Pair<String, Predicate<T>>> rules = new ArrayList<>();
public void addRule(String name, Predicate<T> rule) {
rules.add(Pair.of(name, rule));
}
public List<String> validate(T input) {
return rules.stream()
.filter(p -> !p.getRight().test(input))
.map(Pair::getLeft)
.collect(Collectors.toList());
}
}
对于特别复杂的业务规则,可以创建自定义复合Predicate:
java复制public class CompositePredicate<T> implements Predicate<T> {
private final List<Predicate<T>> predicates;
public CompositePredicate(List<Predicate<T>> predicates) {
this.predicates = predicates;
}
@Override
public boolean test(T t) {
return predicates.stream().allMatch(p -> p.test(t));
}
public CompositePredicate<T> and(Predicate<T> other) {
List<Predicate<T>> newPredicates = new ArrayList<>(predicates);
newPredicates.add(other);
return new CompositePredicate<>(newPredicates);
}
}
在百万级数据集的测试中,Predicate配合Stream的并行处理展现出明显优势:
java复制// 传统方式
List<Result> results = new ArrayList<>();
for (Data data : dataset) {
if (data.isValid() && data.isRelevant()) {
results.add(process(data));
}
}
// Predicate方式
List<Result> results = dataset.parallelStream()
.filter(Data::isValid)
.filter(Data::isRelevant)
.map(this::process)
.collect(Collectors.toList());
测试结果显示,后者在处理大数据量时通常有20%-30%的性能提升,代码也更加简洁清晰。