Introduction
In java terms, Predicate
is an interface or more precisely, a Functional Interface added in java 8.
In simple terms, a predicate is an expression that tests for a certain condition and returns boolean true
or false
depending on whether the condition matches or not.
Example, checking if a string equals certain characters or a number matches some value.
In this article, we will be looking at Predicate
usage, representing it as a Lambda expression, using Predicate in java streams, chaining predicates and much more.
Predicate
interface belongs to java.util.function
package. It is a Functional Interface with a single method test()
which takes one argument and returns a boolean
value.Thus, any expression which returns a
boolean
can be represented as a Predicate
.Creating and applying Predicate
Predicate
is an interface so you can not create its object using any method.Since it is a functional interface, it can be represented as a Lambda expression which takes a single argument and returns a
boolean
as shown below. Predicate<String> p = str -> str.equals(“abc”);
This predicate checks if a string value is equal to “abc” and returns true
if it is, false
otherwise.
str
in the predicate need not to be declared before, it is just a variable local to Lambda expression and can be anything.
Now, how do you use this predicate.
By using its test()
method as below
Predicate<String> p = str -> str.equals("abc"); String toBeTested = "def"; // test predicate with another string boolean result = p.test(toBeTested); if(result) { System.out.println("Strings matched"); } else { System.out.println("Strings do not match"); }
Now you have a reusable Predicate
which compares a string with “abc”.
If you want to create a method that takes a string argument and compares it with “abc”, then using Predicate
, you can write it as below.
public boolean match(String s) { Predicate<String> p = str -> str.equals("abc"); return p.test(s); }
Note that since we are matching string values, the type of Predicate
is a String
. If we want to match integer values, then change the type of Predicate
to Integer
or use an IntPredicate.
Below illustration also shows the control flow of test()
.
Predicates in stream
Predicates are used in java stream api to filter out stream elements based on a condition.
filter()
method of Stream
takes an argument of type Predicate
since it needs to select elements based on some condition.
That is what Predicate
is designed for, to evaluate a condition.
Below is the signature of filter()
method in java.util.stream.Stream
interface.
Stream<T> filter(Predicate<? super T> predicate);
From the method signature, it is evident that the type of Predicate
should be the same as the type of stream.
Suppose you have a list of Student objects and you want to get only those students whose age is greater than 15 years.
Following stream will use Predicate
for this as below.
//create a predicate as per the condition Predicate<Student> p = p -> p.getAge() > 15; // filter records List<Student> studentsGreaterThan15 = students. filter(p). collect(Collectors.toList());
Above code can more precisely be written as
List<Student> studentsGreaterThan15 = students. filter(st -> st.getAge() > 15). collect(Collectors.toList());
Till now, we looked at Predicate examples which match a single condition but what if we want to match multiple conditions such as we do in if-else or switch statements.
Predicates can be chained when there are multiple conditions need to be checked using its and()
and or()
methods which behave in the same way as logical AND(&&
) and OR(||
) operators respectively.
Suppose you want to check if a number lies between 10 and 20. A Predicate
example for this condition can be written as below.
// create a greater than predicate Predicate<Integer> greaterThanPredicate = num -> num > 10; // create a lesser than predicate Predicate<Integer> lesserThanPredicate = num -> num < 20; // create a range predicate Predicate<Integer> range = greaterThanPredicate.and(lesserThanPredicate); int numberToCheck = 50; // check if number lies between range boolean result = range.test(numberToCheck); // will be false if (result) { System.out.println(numberToCheck + " lies between 10 and 20"); } else { System.out.println(numberToCheck + " does not lie between 10 and 20"); }
Notice the use of and()
method to join or chain predicates. You can join any number of predicates this way.
Above code can be shortened to
// create a greater than predicate Predicate<Integer> greaterThanPredicate = num -> num > 10; int result = greaterThanPredicate.and(num -> num < 20).test(50);
Similarly, if you want to check if a color is red or green, following Predicate
can be written.
Predicate<String> red = c -> c.equals("red"); Predicate<String> green = c -> c.equals("red"); Predicate<String> color = red.or(green); boolean result = color.test("yellow"); // returns false boolean result = color.test("red"); // returns true
When test()
is invoked on predicates joined with or()
, it will return true
if any of the predicates returns true
.
Negate Predicate
A Predicate
condition can be reversed using its negate()
method. This is similar to applying a NOT(!
) operator before a condition.
Suppose you want to check if a string does not contain certain characters, this can be written using a Predicate
with negate()
method as
// create a predicate Predicate<String> containsCheck = str -> str.contains("valid"); // reverse it Predicate<String> notContainsCheck = containsCheck.negate(); // check if string does NOT contain "valid" notContainsCheck.test("This is invalid"); // returns true
This example can be shortened as
// create a predicate Predicate<String> containsCheck = str -> str.contains("valid"); // check if string does NOT contain "valid" containsCheck.negate().test("This is invalid"); // returns true
and()
, or()
and negate()
methods return another Predicate
, that is why, it is possible to chain them.
Note that Predicate
is a Functional Interface having only one abstract method test()
.
All other methods such as and()
. or()
, negate()
are default interface methods which are defined in the interface.
Predicate
interface introduced in java 8.