05 - The Predicate Interface

A predicate can be understood as a function that returns a value that is either true or false. The proper use of it can make your code easier to maintain.

Although Predicate shines more when used together with streams, we can actually use it in every situation where we weed to evaluate if an expression returns true or false.

As I have the intention to create a series of posts dedicated exclusively to the streams.api, I’ve been trying to keep streams away from the posts in this lambda series, and this one will be no exception, although the temptation is strong.

First let’s see what we have in the source code of the Predicate interface,

java.util.function.Predicate.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@FunctionalInterface
public interface Predicate<T> {

boolean test(T t);

default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}

default Predicate<T> negate() {
return (t) -> !test(t);
}

default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}

A quick glance at the code reveals almost immediately what it does.
test() returns a boolean and is the abstract method and will define the signature of the lambda expression reference which can be assigned to a Predicate

and() together with or() appear to do the combinatory logic between two predicate functions, negate() as the name implies negates a predicate and isEqual(), is self-explanatory.

To ensure if this works as expected, lets write a quick example,

App.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class App {

public static void main(String[] args) {

Predicate<Integer> onlyOddNumbers = i -> !(i % 2 == 0);

List<Integer> integerList = Arrays.asList(1, 2, 5, 8, 6, -13, 9);
Print(filter(integerList, onlyOddNumbers));

}

public static void Print(List<Integer> listToPrint) {
listToPrint.forEach(System.out::println);
}

public static List<Integer> filter(List<Integer> originalList, Predicate<Integer> predicate) {
List<Integer> result = new ArrayList<Integer>();
for (Integer i : originalList) {
if (predicate.test(i)) {
result.add(i);
}
}
return result;
}
}

We define a Predicate onlyOddNumbers to evaluate if a number is odd or even. We then pass that predicate to the filter() method together with a list of Integers testing all the elements of the list against that predicate while storing the elements that evaluate as true. Then we print the result.

When run, it prints
1
5
-13
9

Great…How about combining predicates ?

Let’s add a combined Predicate to our app, and see how it goes,

App.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class App {

public static void main(String[] args) {

Predicate<Integer> onlyOddNumbers = i -> !(i % 2 == 0);
Predicate<Integer> onlyPositives = i-> i > 0;
//combined predicate
Predicate<Integer> positivesAndEven=onlyOddNumbers.and(onlyPositives);

List<Integer> integerList = Arrays.asList(1, 2, 5, 8, 6, -13, 9);
Print(filter(integerList, positivesAndEven));

}

public static void Print(List<Integer> listToPrint) {
listToPrint.forEach(System.out::println);
}

public static List<Integer> filter(List<Integer> originalList, Predicate<Integer> predicate) {
List<Integer> result = new ArrayList<Integer>();
for (Integer i : originalList) {
if (predicate.test(i)) {
result.add(i);
}
}
return result;
}
}

This code is similar to the previous one. We just added a second Predicate named onlyPositives and combined it with onlyOddNumbers in positivesAndEven. As before, we passed this combinatory predicate to the filter() method and then we printed the result.

When run it prints
1
5
9

The negative value is gone, filtered by the onlyPositives predicate.

In this particular case if we apply or() to combine both predicates we will get all the values from the initial list, due to the fact that an OR operation only returns true when both operands are false (false V false = false). However if we change the value in integerList from -13 to -12, then we won’t get the negative value, as it is negative and even so an OR operation will return false and the value will not be added to the result list.

There isn’t much to say about the negate() or equals(). The first will return the inverse of the Predicate, and the former will compare two objects for equality.

As I mentioned earlier, predicates do shine brighter when used with streams, and I will go into that, as time allows.

The next post in this lambda series will be about the Consumer interface.

‘till then happy coding.

Share