Using BiPredicate – Functional style programming – extending API
186 Using BiPredicate
Let’s consider the Car model and a List<Car> denoted as cars:
public class Car {
private final String brand;
private final String fuel;
private final int horsepower;
…
}
Our goal is to see if the following Car is contained in cars:
Car car = new Car(“Ford”, “electric”, 80);
We know that the List API exposes a method named contains(Object o). This method returns true if the given Object is present in the given List. So, we can easily write a Predicate as follows:
Predicate<Car> predicate = cars::contains;
Next, we call the test() method and we should get the expected result:
System.out.println(predicate.test(car)); // true
We can obtain the same result in a stream pipeline via filter(), anyMatch(), and so on. Here is via anyMatch():
System.out.println(
cars.stream().anyMatch(p -> p.equals(car))
);
Alternatively, we can rely on BiPredicate. This is a functional interface representing a two-arity specialization of the well-known Predicate. Its test(Object o1, Object o2) method gets two arguments, so it is a perfect fit for our case:
BiPredicate<List<Car>, Car> biPredicate = List::contains;
And, we can perform the test as follows:
System.out.println(biPredicate.test(cars, car)); // true
In the next problem, you’ll see a more practical example of using a BiPredicate.
187. Building a dynamic predicate for a custom model
Let’s consider the Car model and a List<Car> denoted as cars:
public class Car {
private final String brand;
private final String fuel;
private final int horsepower;
…
}
And, let’s assume that we need to dynamically produce a wide range of predicates that applies the operators <, >, <=, >=,!=, and == to the horsepower field. It will be cumbersome to hardcode such predicates, so we have to come up with a solution that can build on the fly any predicate that involves this field and one of the comparison operators listed here.There are a few approaches to accomplish this goal, and one of them is to use a Java enum. We have a fixed list of operators that can be coded as enum elements as follows:
enum PredicateBuilder {
GT((t, u) -> t > u),
LT((t, u) -> t < u),
GE((t, u) -> t >= u),
LE((t, u) -> t <= u),
EQ((t, u) -> t.intValue() == u.intValue()),
NOT_EQ((t, u) -> t.intValue() != u.intValue());
…
In order to apply any of these (t, u) lambdas, we need a BiPredicate constructor (see Problem 186) as follows:
private final BiPredicate<Integer, Integer> predicate;
private PredicateBuilder(
BiPredicate<Integer, Integer> predicate) {
this.predicate = predicate;
}
…
Now that we can define a BiPredicate, we can write the method that contains the actual test and returns a Predicate<T>:
public <T> Predicate<T> toPredicate(
Function<T, Integer> getter, int u) {
return obj -> this.predicate.test(getter.apply(obj), u);
}
…
Finally, we have to provide here the Function<T, Integer> which is the getter corresponding to horsepower. We can do this via Java Reflection as follows:
public static <T> Function<T, Integer> getFieldByName(
Class<T> cls, String field) {
return object -> {
try {
Field f = cls.getDeclaredField(field);
f.setAccessible(true);
return (Integer) f.get(object);
} catch (IllegalAccessException | IllegalArgumentException
| NoSuchFieldException | SecurityException e) {
throw new RuntimeException(e);
}
};
}
Of course, it can be any other class and integer field as well, not only the Car class and the horsepower field. Based on this code, we can dynamically create a predicate as follows:
Predicate<Car> gtPredicate
= PredicateBuilder.GT.toPredicate(
PredicateBuilder.getFieldByName(
Car.class, “horsepower”), 300);
Using this predicate is straightforward:
cars.stream()
.filter(gtPredicate)
.forEach(System.out::println);
You can use this problem as an inspiration point for implementing more types of dynamic predicates. For example, in the next problem, we use the same logic in another scenario.