Java中函数式编程的谓词函数(Predicates)

什么是谓词函数?

Apache Commons Collections里的谓词函数仅仅只是一个只有一个方法的接口:

evaluate(Object object): boolean

这就是谓词函数,输入一个对象,返回true或false。在Google Guava中,定义了Predicate接口,该接口包含一个带有泛型参数的方法:

apply(T input): boolean

如果想在程序中使用谓词函数,只需要利用自己的逻辑实现该接口即可。

例子:

假设有一个订单列表,每个订单用PurchaseOrder表示,PurchaseOrder中包含日期、顾客和状态。不同的用例要求你有不同的输出,比如获取某个顾客所有等待发货、已发货、已交付或者过去一个小时内完成的订单。当然可以在循环中使用if判断实现这些功能:

//List<PurchaseOrder> orders...
public List<PurchaseOrder> listOrdersByCustomer(Customer customer) {
    final List<PurchaseOrder> selection = new ArrayList<PurchaseOrder>();
    for (PurchaseOrder order : orders) {
        if (order.getCustomer().equals(customer)) {
            selection.add(order);
        }
    }
    return selection;
}
以上是获取某个客户所有的订单的代码。不同的功能需要编写多个类似的循环:

public List<PurchaseOrder> listRecentOrders(Date fromDate) {
        final List<PurchaseOrder> selection = new ArrayList<PurchaseOrder>();
        for (PurchaseOrder order : orders) {
            if (order.getDate().after(fromDate)) {
                selection.add(order);
            }
        }
        return selection;
}
这些重复的代码非常明显:除了if的判断条件之外没有任何差异。采用谓词函数的思想在于,利用传入到函数内的谓词的调用替代if语句块里的硬编码的判断条件。这就意味着,你只需要编写带有谓词函数作为参数的方法,就可覆盖所有的甚至你还不知道的测试用例:

public List<PurchaseOrder> listOrders(Predicate<PurchaseOrder> condition) {
    final List<PurchaseOrder> selection = new ArrayList<PurchaseOrder>();
    for (PurchaseOrder order : orders) {
        if (condition.apply(order)) {
            selection.add(order);
        }
    }
    return selection;
}
如果需要考虑到复用,则可以将谓词函数声明成一个单独的类,否则可以把谓词声明成匿名类:

final Customer customer = new Customer("BruceWaineCorp");
final Predicate<PurchaseOrder> condition = new Predicate<PurchaseOrder>() {
    public boolean apply(PurchaseOrder order) {
        return order.getCustomer().equals(customer);
    }
};
Apache或Google的API提供了一个类似java.util.Collections的命名为Collections2的类,它提供了与我们先前编写的代码功能类似的filter()函数,所以可以将方法重构成无循环的版本:

public Collection<PurchaseOrder> selectOrders(Predicate<PurchaseOrder> condition) {
    return Collections2.filter(orders, condition);
}
在一个类似的场景中,我们可以要求返回在给定的迭代器之上过滤好的只符合谓词函数的元素的迭代器(装饰模式)。

Iterator filteredIterator = Iterators.filter(unfilteredIterator, condition);
Iterable接口和循环使用起来非常方便:

public Iterable<PurchaseOrder> selectOrders(Predicate<PurchaseOrder> condition) {
    return Iterables.filter(orders, condition);
}
// you can directly use it in a foreach loop, and it reads well:
for (PurchaseOrder order : orders.selectOrders(condition)) {
    //...
}


为了使用谓词,我们必须声明自己的谓词接口,或者为应用程序中使用到的谓词参数都声明一个类。然而从类似Guava以及Commons的API中使用标准谓词接口的好处是:你可以结合这类API提供的大量优秀组件实现你自己的谓词函数。
如果你需要的是判断一个对象是否为空或者不为空的条件,你不需要自己实现一个谓词函数,只需要使用现成的谓词就可以了:

// gives you a predicate that checks if an integer is zeroPredicate
<Integer> isZero = Predicates.equalTo(0);
// gives a predicate that checks for non null objects
Predicate<String> isNotNull = Predicates.notNull();
// gives a predicate that checks for objects that are instanceof the given Class
Predicate<Object> isString = Predicates.instanceOf(String.class);
对于给定的谓词,你可以反转它(返回相反的返回值,比如true变成false):

Predicates.not(predicate);
利用AND、OR操作结合多个谓词:

Predicates.and(predicate1, predicate2);
Predicates.or(predicate1, predicate2);
// gives you a predicate that checks for either zero or null
Predicate<Integer> isNullOrZero = Predicates.or(isZero, Predicates.isNull());








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值