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.
Leave a Reply