A Predicate is one of the most useful functional interfaces in Java. It represents a condition that accepts one input value and produces a boolean result: true or false.
Whenever your program needs to answer a yes-or-no question about an object, a Predicate is often a natural fit. Is the number even? Is the user active? Is the product in stock? Does the student's mark exceed 60? These are all predicate-style questions.
What Is Predicate?
The Predicate<T> interface belongs to the java.util.function package.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
The important part is the test() method. It accepts one value of type T and returns a boolean.
Remember: Predicate means "test a condition." Its primary operation is test(), and the result is always true or false.
Basic Predicate Example
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
Predicate<Integer> isEven =
number -> number % 2 == 0;
System.out.println(isEven.test(10));
System.out.println(isEven.test(7));
}
}
The lambda checks whether a number is divisible by 2. When 10 is passed to test(), the result is true. When 7 is passed, the result is false.
Understanding Predicate<T>
The letter T represents the type of value that the Predicate accepts.
| Predicate | Input | Result | Method |
|---|---|---|---|
| Predicate<Integer> | Integer | boolean | test() |
| Predicate<String> | String | boolean | test() |
| Predicate<Student> | Student | boolean | test() |
The output type does not change. A Predicate always answers a boolean question.
Predicate with Strings
Predicates are not limited to numbers. You can use them with strings as well.
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
Predicate<String> hasLongName =
name -> name.length() > 5;
System.out.println(hasLongName.test("Rahul"));
System.out.println(hasLongName.test("Ananya"));
}
}
The Predicate checks whether the supplied string contains more than five characters.
Predicate with Custom Objects
One of the most practical uses of Predicate is testing properties of application objects.
import java.util.function.Predicate;
class Student {
String name;
int marks;
Student(String name, int marks) {
this.name = name;
this.marks = marks;
}
}
public class Main {
public static void main(String[] args) {
Predicate<Student> passed =
student -> student.marks >= 40;
Student student = new Student("Ravi", 68);
System.out.println(passed.test(student));
}
}
The Predicate represents the rule for deciding whether a student has passed. The student object is supplied later through test().
Predicate as a Method Parameter
Instead of creating a Predicate only to test one value, you can pass it to a method.
import java.util.function.Predicate;
public class Main {
static void check(int number, Predicate<Integer> condition) {
if (condition.test(number)) {
System.out.println("Condition matched");
} else {
System.out.println("Condition did not match");
}
}
public static void main(String[] args) {
check(20, number -> number > 10);
check(7, number -> number % 2 == 0);
}
}
The method does not need to know what the condition is. It simply executes the Predicate. This makes the method reusable with different rules.
Predicate with Collections
Predicates become especially powerful when working with collections and streams.
import java.util.*;
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
List<Integer> numbers =
Arrays.asList(10, 15, 20, 25, 30);
Predicate<Integer> greaterThan20 =
number -> number > 20;
numbers.stream()
.filter(greaterThan20)
.forEach(number -> System.out.println(number));
}
}
The filter() operation expects a Predicate. Each number is passed to test(), and only values that produce true continue through the stream.
Using Predicate Directly with filter()
You do not always need to create a separate Predicate variable.
numbers.stream()
.filter(number -> number > 20)
.forEach(number -> System.out.println(number));
Here, the lambda itself acts as the Predicate because filter() expects a Predicate as its argument.
Combining Predicates
A particularly useful feature of Predicate is that multiple conditions can be combined. Java provides methods such as and(), or(), and negate().
Predicate and()
The and() method combines two predicates. Both conditions must be true for the final result to be true.
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
Predicate<Integer> greaterThan10 =
number -> number > 10;
Predicate<Integer> even =
number -> number % 2 == 0;
Predicate<Integer> condition =
greaterThan10.and(even);
System.out.println(condition.test(20));
System.out.println(condition.test(15));
}
}
The value must be greater than 10 and even. Therefore, 20 passes both conditions, while 15 fails the even-number condition.
Predicate or()
The or() method produces true when at least one of the predicates is true.
Predicate<Integer> positive =
number -> number > 0;
Predicate<Integer> zero =
number -> number == 0;
Predicate<Integer> valid =
positive.or(zero);
System.out.println(valid.test(10));
System.out.println(valid.test(0));
System.out.println(valid.test(-5));
A value is accepted when it is either positive or zero.
Predicate negate()
The negate() method reverses the result of a Predicate.
Predicate<Integer> even =
number -> number % 2 == 0;
Predicate<Integer> odd =
even.negate();
System.out.println(odd.test(7));
System.out.println(odd.test(10));
The original Predicate returns true for even numbers. Calling negate() creates another Predicate that returns true for values that do not satisfy the original condition.
Combining Multiple Conditions
Predicates can be combined to represent business rules without placing all the logic into one large lambda.
Predicate<Integer> ageAtLeast18 =
age -> age >= 18;
Predicate<Integer> ageBelow60 =
age -> age < 60;
Predicate<Integer> workingAge =
ageAtLeast18.and(ageBelow60);
System.out.println(workingAge.test(30));
This style is useful when conditions have meaningful names. It makes complex rules easier to read, test, and maintain.
Predicate isNotNull()
The Predicate interface also provides a static method called not() in modern Java versions, which can make negated conditions easier to express.
import java.util.function.Predicate;
Predicate<String> isNotEmpty =
Predicate.not(String::isEmpty);
This creates a Predicate that accepts strings for which isEmpty() returns false.
Predicate vs Boolean Expression
A boolean expression produces a result immediately, while a Predicate represents a reusable condition.
int age = 25; boolean result = age >= 18;
The expression checks the current value immediately.
Predicate<Integer> adult =
value -> value >= 18;
The Predicate stores the condition so it can be applied to different values later.
| Boolean Expression | Predicate |
|---|---|
| Produces a boolean immediately | Represents reusable test logic |
| Works with the current value | Can test many values |
| Cannot be passed as behavior directly | Can be passed to methods |
Common Beginner Mistakes
- Forgetting that Predicate always returns a boolean result.
- Calling apply() instead of the correct test() method.
- Using Predicate when the operation needs to return a transformed value instead of a condition.
- Writing one extremely complicated lambda instead of combining smaller named predicates.
- Confusing a Predicate with a boolean variable.
Best Practices
- Use Predicate when the main question has a true-or-false answer.
- Give reusable predicates meaningful names.
- Combine predicates with and(), or(), and negate() when it improves readability.
- Use Predicate with stream filter() for clean collection processing.
- Keep individual predicates focused on one clear condition.
Interview Insight
The key interview point is simple: Predicate<T> accepts one argument of type T and returns a boolean through its test() method. It is commonly used for filtering and condition checking.
Quick Revision
| Feature | Purpose | Example |
|---|---|---|
| Predicate<T> | Represents a condition | Predicate<Integer> |
| test() | Evaluates the condition | predicate.test(10) |
| and() | Requires both conditions to be true | first.and(second) |
| or() | Requires at least one condition to be true | first.or(second) |
| negate() | Reverses the condition | predicate.negate() |
| filter() | Uses a Predicate to select values | stream.filter(predicate) |
Final Takeaway
A Predicate is Java's standard way of representing reusable yes-or-no logic. It accepts one value and returns a boolean through test(). Its real strength appears when it is passed to methods, used with streams, or combined with other predicates. Once you start thinking of conditions as reusable pieces of behavior rather than scattered boolean expressions, your Java code becomes easier to reuse, compose, and maintain.
