我如何结合BiPredicate和Predicate?

时间:2015-06-12 08:25:08

标签: java lambda functional-programming java-8

我有两个lambda函数(谓词):

final Predicate<Node> isElement = node -> node.getNodeType() == Node.ELEMENT_NODE;
final BiPredicate<Node, String> hasName = (node, name) -> node.getNodeName().equals(name);

我希望以一种简洁的方式结合使用,如下所示:

// Pseudocode
isElement.and(hasName("tag")) // type of Predicate

然后传递给另一个lambda函数:

final BiFunction<Node, Predicate<Node>, List<Node>> getChilds = (node, cond) -> {
    List<Node> resultList = new ArrayList<>();
    NodeList nodeList = node.getChildNodes();
    for (int i = 0; i < nodeList.getLength(); ++i) {
        Node tmp = nodeList.item(i);
        if (cond.test(tmp)) {
            resultList.add(tmp);
        }
    }
    return resultList;
};

结果我期待它看起来如下:

List<Node> listNode = getChilds.apply(document, isElement.and(hasName("tag")));

and的{​​{1}}方法不接受Predicate参数。

我怎么能这样做?

3 个答案:

答案 0 :(得分:5)

停止将每个方法重写为lambda表达式。没有真正的好处。如果您使用普通方法,则可以通过简单名称调用它,而无需附加applytest或类似内容。如果您确实需要一个函数,那么仍然可以使用::运算符创建静态方法引用。

因此,如果您想改进代码,请考虑使用新的 API 而不是过度使用Java语言功能。例如:

static List<Node> getChilds(Node node, Predicate<Node> cond) {
    NodeList nodeList = node.getChildNodes();
    return IntStream.range(0, nodeList.getLength()).mapToObj(nodeList::item)
                    .filter(cond).collect(Collectors.toList());
}

关于您尝试合并Predicate的问题。当然,你可以表达所有功能而不妥协。例如:

Predicate<Node> isElement = node -> node.getNodeType() == Node.ELEMENT_NODE;
Function<Node, String> nodeName = Node::getNodeName;
Predicate<Node> both = isElement.and(nodeName.andThen("tag"::equals)::apply);

但这真的是一种改进吗?

您可以简单地写

Predicate<Node> both = isElement.and(n -> n.getNodeName().equals("tag"));

或更简单,因为Node不代表ELEMENT个节点,永远不会报告"tag"的节点名称,根本不需要第一个谓词和整个谓词操作变为:

getChilds(document, n -> "tag".equals(n.getNodeName()));

这可能不像复杂的功能组合那样花哨,但它是实用的解决方案。

答案 1 :(得分:4)

你需要编写一个静态辅助函数,将你的BiPredicate curries下移到一个谓词,然后你可以.and()使用另一个谓词。

isElement.and(curryRight(hasName, "tag"))

或者,您可以这样做:

isElement.and(node -> hasName.test(node, "tag"))

它不会那么长

答案 2 :(得分:3)

将lambada函数hasName更改为函数hasName(String name)返回lambda函数。

final Predicate<Node> hasName(String name) { return node -> node.getNodeName().equals(name); }

然后你的代码就可以了。

List<Node> listNode = getChilds.apply(document, isElement.and(hasName("tag")));