I have two lambda functions (predicates):
final Predicate<Node> isElement = node -> node.getNodeType() == Node.ELEMENT_NODE;
final BiPredicate<Node, String> hasName = (node, name) -> node.getNodeName().equals(name);
Which I want to combine in some concise way, something like this:
// Pseudocode
isElement.and(hasName("tag")) // type of Predicate
and then pass to another lambda function:
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;
};
As a result I'm expecting it would look like the following:
List<Node> listNode = getChilds.apply(document, isElement.and(hasName("tag")));
But and
method of Predicate
doesn't accept BiPredicate
parameter.
How I could do this?
A Predicate<T> interface defined in java.util.function package. It represents a boolean-valued function with one argument. It is kind of a functional interface whose functional method is the test (). BiPredicate<T, U> interface is similar to the Predicate<T> interface with two arguments.
For example, we can use predicates in these real-life usecases: and so on… 1.2. Using Predicate with Stream As we know, Predicate is a functional interface that means we can pass it in lambda expressions wherever a predicate is expected. For example, one such method is filter () method from Stream interface.
We can also combine more than one predicate to make a predicate chain or complex predicate, as we do in builder pattern. So, in this function, we pass the list of employees and we pass a predicate, then this function will return a new collection of employees satisfying the condition mentioned in parameter predicate.
It represents a boolean-valued function with one argument. It is kind of a functional interface whose functional method is the test (). BiPredicate<T, U> interface is similar to the Predicate<T> interface with two arguments. It can be used as an assignment target for a lambda expression.
Stop rewriting every method into a lambda expression. There is no real benefit. If you have an ordinary method, you can call it by its simple name without having to append apply
, test
or similar. If you really need a function then, you can still create a static method reference using the ::
operator.
So if you want to improve you code, think about using the new API instead of overusing the Java language features. For example:
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());
}
Regarding you attempt to combine Predicate
s. Of course, you can express everything as a function without compromise. For example:
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);
but is this really an improvement?
You may simply write
Predicate<Node> both = isElement.and(n -> n.getNodeName().equals("tag"));
or, even simpler, since Node
s not representing ELEMENT
nodes will never report a node name of "tag"
you don't need the first predicate at all and the entire operation becomes:
getChilds(document, n -> "tag".equals(n.getNodeName()));
That might not feel as fancy as complicated function composition, but it's a practical solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With