BiPredicate<T, U> is Java’s standard functional interface for a condition that takes two inputs and returns a boolean. Use its test(T, U) method to evaluate a pair, then use and, or, or negate to compose conditions. It has been part of Java since Java 8.
What BiPredicate does
Oracle describes BiPredicate as a “predicate (boolean-valued function) of two arguments.” Its type parameters can be different reference types, so a condition can depend on, for example, a name and an age or a key and a value. The functional method is test(T, U), which returns a boolean. See the Java SE 25 BiPredicate API.
As an Amazon Associate I earn from qualifying purchases.
import java.util.function.BiPredicate;
BiPredicate<String, Integer> nameAndAge =
(name, age) -> name.startsWith("A") && age >= 18;
boolean allowed = nameAndAge.test("Ari", 21); // true
The lambda’s first parameter is a String, its second is an Integer, and its result is true only when both conditions hold. Calling test runs the condition with the supplied pair.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →When to use BiPredicate instead of Predicate
Use Predicate<T> when a condition needs one input. Use BiPredicate<T, U> when the condition naturally depends on two. Their functional methods have different arities: Predicate.test(T) takes one argument, while BiPredicate.test(T, U) takes two. A BiPredicate therefore is not directly interchangeable with an API that expects a Predicate.
If an API processes one value at a time but your rule also needs a fixed second value, adapt the rule with a lambda that captures that value. For example, a stream of names can use name -> nameAndAge.test(name, age). That is an adaptation in your code, not a built-in conversion between the interfaces.
How to combine BiPredicate conditions
The interface provides and, or, and negate. Name component predicates when that makes the rule easier to read:
Rank #2
BiPredicate<String, Integer> hasName =
(name, age) -> name != null && !name.isBlank();
BiPredicate<String, Integer> isAdult =
(name, age) -> age >= 18;
BiPredicate<String, Integer> eligible = hasName.and(isAdult);
boolean result = eligible.test("Ari", 21); // true
hasName checks for null before calling isBlank, avoiding a null dereference for that input. Composition does not make the component logic safe automatically; each predicate must handle the values it can receive.
AND: require both conditions
left.and(right) is a short-circuiting logical AND. Java evaluates left first and evaluates right only if left returns true. This is useful when the second condition depends on the first check having succeeded, as with validating a nullable value before inspecting it.
OR: accept either condition
left.or(right) is a short-circuiting logical OR. Java evaluates right only when left returns false. For example, isAdmin.or(isOwner) does not call isOwner when isAdmin is already true.
Negation: invert the result
predicate.negate() returns a predicate whose result is the logical opposite of the original. For instance, hasName.negate() is true when hasName is false.
Rank #4
Evaluation order, exceptions, and null arguments
For and and or, the left predicate runs first and short-circuiting determines whether the right one runs. If an evaluated predicate throws an exception, that exception reaches the caller; composition does not catch it. Avoid relying on hidden side effects in predicate bodies, because short-circuiting means a component may not run on every call.
Passing null as the other predicate to and or or throws NullPointerException. This is distinct from passing null as one of the values to test: whether a value is acceptable depends on what the supplied lambda does. The API documents the composition behavior in the BiPredicate method contract.
Best Value
Primitive values and BiPredicate
Java has primitive specializations for some single-input predicates, such as IntPredicate, but it does not provide a specialized primitive BiPredicate interface. The standard BiPredicate<T, U> uses reference-type parameters; using it with primitive values involves their boxed types, such as Integer. Dev.java discusses functional interfaces and primitive specializations in Using Lambda Expressions in Your Application.
Quick Recap
Keep composed rules readable
- Give predicates meaningful names when a combined rule would otherwise become difficult to scan.
- Use
andwhen the second check should run only after the first succeeds, andorwhen it is needed only after the first fails. - Handle nullable inputs inside the component that uses them, and order checks so short-circuiting protects later operations where appropriate.
- Keep predicates focused on returning a boolean rather than relying on side effects.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




