Generally predicate means a statement that determines whether a value could be true or false. In programming predicates mean functions with one argument that return a boolean value.
Functional Interface Predicate was realised in Java 8. “Functional” means that it contains only one abstract method. It accepts an argument and returns a boolean.
In Java, functional interfaces are used for handling Lambda expressions, constructors, and Method references.
Usually Java 8 Predicate is used to apply a filter for a collection of objects. Let’s take a look at its main applications and widely used methods, as well as solve some practice problems.
Why Developers Use Predicate
Basically, developers can use predicates for any tasks that involve evaluating items based on predefined criteria and returning a boolean value. Here are the examples of simple tasks developers handle using predicates:- Filtering a set of integers.
- Sorting lists by ensuring that the data is compliant with several predefined conditions (e.g. organizing a range of items by setting price and weight conditions).
- Using utility packages in concurrent programming.
Predicate Syntax in Java
java.util.function.Predicate was introduced in Java 8 as an alternative way to handle assessment impressions in Lambda. The standard view of the interface is Predicate<T>, where T is a single argument returning a boolean value. Java Predicates have a functional (abstract) method test(Object) that evaluates this predicate on a given Object.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Here’s an example of writing a simple Predicate that filters integers based on conditions “greater than”, “lesser than”.
// An example of a simple Java predicate
import java.util.function.Predicate;
public class PredicateExample {
public static void main(String[] args)
{
// Creating predicate
Predicate<Integer> lesserThan = i -> (i < 18);
// Calling Predicate method
System.out.println(lesserThan.test(10));
}
}
The output will be true because 10 < 18.
One more example with Predicate in filter(). Predicate helps to filter all adults from the list of ages.
import java.util.List;
import java.util.function.Predicate;
public class PredicateExample {
public static void main(String[] args) {
List<Integer> ages = List.of(17, 18, 19, 28, 18, 28, 46, 7, 8, 9, 21, 12);
NotLessThan18<Integer> isAdult = new NotLessThan18<>();
ages.stream().filter(isAdult).forEach(System.out::println);
}
}
class NotLessThan18<E> implements Predicate<Integer> {
@Override
public boolean test(Integer v) {
Integer ADULT = 18;
return v >= ADULT;
}
}
The output is:
18
19
28
18
28
46
21
Java 8 Predicate Methods
The Predicate Interface has a handful of methods.- boolean test(T t) evaluates the predicate on the given argument.
- default Predicate<T> and(Predicate<? super T> other) returns a predicate that represents a short-circuiting logical AND of this predicate and another.
- default Predicate<T> or returns a composed predicate that represents a short-circuiting logical OR of this predicate and another.
- default Predicate<T> negate() returns a predicate that is opposite to this predicate logically.
- default Predicate<T> isEqual(Object targetRef) returns a result of testing if two arguments are equal according to Objects.equals(Object, Object).
boolean test(T t)
This is a functional method for Java predicates that evaluates whether or not a given argument satisfies the condition of a predicate. Example: here we create a predicate adult for everyone who is 18 or more years old. test() method gets an integer value and checks it.
import java.util.function.Predicate;
public class PredicateTestTest {
public static void main(String[] args) {
Predicate<Integer> adult = i -> i >= 18;
System.out.println(adult.test(12));
System.out.println(adult.test(19));
System.out.println(adult.test(21));
}
}
What will the output for the code above be? In the first case, since 12 is lesser than 18, it will be false. As for the second and the third scenario, the conditions are met so the return will be true.
false
true
true
default Predicate.and()
This method represents the “and” operator. That’s why, if one of the given predicates doesn’t comply with a set condition, another one will not get an evaluation. Example: Let’s compose predicates in Java, filtering all people who are already adult but younger than 65 using and(). Let’s use predicate.add () and write a java predicate with lambda for these conditions: If the condition is true, the app will return the following statement:
import java.util.function.Predicate;
public class PredicateDemo {
public static void main(String[] args) {
Predicate<Integer> adultYet = i -> i >= 18;
Predicate<Integer> adultStill = i -> i < 65;
System.out.println(adultYet.and(adultStill).test(5));
System.out.println(adultYet.and(adultStill).test(38));
System.out.println(adultYet.and(adultStill).test(90));
}
}
The output is:
false
true
false
default Predicate.or()
Predicate.or() method represents the “OR” operator. This means that the condition will stay true even if one of two predicates is true and the other one is false. Example: let’s evaluate character strings now. Try using the OR method to sort through all phrases that contain substring “my” or “crayon”.
import java.util.function.Predicate;
public class PredicateDemo2 {
public static void main(String[] args) {
Predicate<String> containsA = t -> t.contains("crayon");
Predicate<String> containsB = t -> t.contains("my");
System.out.println(containsA.or(containsB).test("here is my crayon"));
System.out.println(containsA.or(containsB).test("here is my pencil"));
System.out.println(containsA.or(containsB).test("here is John's crayon"));
System.out.println(containsA.or(containsB).test("here is John's pencil"));
}
}
The output is:
true
true
true
false
default Predicate negate()
negate() method is used to gather all values that don’t comply with predefined criteria. Example: if you want to sort through the class “Tomatoes” and find all entries that are not “Red”, you can write a Predicate and quickly scan through the entire sequence. Try writing the code for this one on your own and check it against the solution once you are done.
import java.util.function.Predicate;
public class PredicateDemo3 {
public static void main(String[] args) {
Predicate<Integer> adult = i -> i >= 18;
System.out.println(adult.negate().test(7)); System.out.println(adult.negate().test(19))
}
}
The output is:
true
false
static Predicate isEqual(Object targetRef)
This method is highly useful if you want to determine whether two objects are equal to a value defined as a parameter of Objects.equals(). This method is especially useful if you need to compare similar testing outcomes. Example: let’s say you are comparing two carriages of pears and want to make sure if both have fruits of a standard weight and color. In this case, static Predicate isEqual(Object targetRef) is exactly the method you need. Let’s take a look at how isEqual checks the equality of two strings:
import java.util.function.Predicate;
public class PredicateDemo2 {
public static void main(String[] args) {
Predicate<String> i = Predicate.isEqual("here is my crayon");
System.out.println(i.test("here is my pencil"));
System.out.println(i.test("here is my crayon"));
}
}
If an object you are analyzing complies with the standard conditions, the function will return true. The output is:
false
true
Java IntPredicate
Java IntPredicate is a functional interface, so you can use it as the assignment target for a lambda expression or method reference. IntPredicate operates on an integer and returns a predicate value based on a condition. Such as Predicate Interface, IntPredicate also has test(), and(), negate(), or() methods. Here’s an example of IntPredicate. It also filters all the adults (18 or more) from the array.
import java.util.Arrays;
import java.util.function.IntPredicate;
public class IntPredicateExample {
public static void main(String[] args) {
int[] ages = { 18, 28, 18, 46, 90, 45, 2, 3, 1, 5, 7, 21, 12 };
IntPredicate p = n -> n >= 18;
Arrays.stream(ages).filter(p).forEach(System.out::println);
}
}
The output is:
18
28
18
46
90
45
21
Writing Clean Predicates in Java
Java Predicates are highly functional and fun to work with. However, unless you are mindful of how lambda expressions you write impact the code, there’s a risk of reducing code maintainability with messy predicates. Here are a few simple practices that will help you ensure your functional interfaces are easy to manage and read.- Don’t Repeat Yourself — don’t compose predicates with Java with repeated values, methods, and conditions more than once. This way, you waste your productive time and make the code messy.
- Separate predicates from the app code to ensure testability. Other than that, create a predicate unit testing schedule and stick to it.
- Use imports and composition patterns to ensure your classes aren't’ bloated and stay easy to manage.
- Consider moving Java predicates to Helper classes — this way, you improve the reusability of your code and facilitate maintenance.
- Readability — whenever possible, prefer one-line statements over complex predicates. It might be tempting to show off your understanding of complex functional interfaces. However, when it comes to maintenance, less is more.
GO TO FULL VERSION