Hook: Interviewers love this one because a tiny interface reveals whether you understand lambdas, composition, and clean filtering in Java 8.
Question: Predicate interface.
Answer: Predicate<T> is a Java 8 functional interface that represents a yes/no test for one input. Its main method is test(T t), and you use it for filtering, validation, and rule checks. It also provides and, or, and negate so you can combine conditions cleanly instead of writing nested if statements.
Interview-Ready Answer: A Predicate is a Java 8 functional interface for one-argument boolean checks. I use test() to answer questions like whether a customer is eligible, and I can combine rules with and, or, and negate. A nice detail is that it works naturally with streams for filtering, and for primitive values there are specialized versions like IntPredicate to avoid boxing.
Predicate<T> is a functional interface in java.util.function introduced in Java 8. A functional interface has exactly one abstract method, so it can be implemented with a lambda or a method reference. For Predicate, that method is boolean test(T t) — it answers a simple yes/no question about one object. It also ships with useful helpers: and, or, negate, and the static helper isEqual.
user -> user.getAge() >= 18.test(T) shape: one input, boolean output.invokedynamic and LambdaMetafactory to create the implementation. You do not write a concrete class yourself.and or or, Java evaluates left to right and short-circuits: the second predicate runs only when needed.stream().filter(predicate).if statements.| Interface | Input | Output | Typical use |
|---|---|---|---|
Predicate<T> | 1 | boolean | test / filter |
Function<T, Boolean> | 1 | Boolean | transform |
Consumer<T> | 1 | void | side effects |
Supplier<T> | 0 | T | provide value |
Predicate is usually the best fit when you only need yes/no. Compared with Function<T, Boolean>, it says your intent clearly and avoids wrapping the result in a boxed Boolean just to represent a condition. If you work with primitive values in hot code, prefer IntPredicate, LongPredicate, or DoublePredicate to avoid boxing overhead. If you need two inputs, Java also gives you BiPredicate<T, U>.
One predicate test is O(1); filtering n items is O(n). Composition does not change the big-O cost, but short-circuiting can save work because the right side may never run. In a list of 1,000,000 items, that still means 1,000,000 calls to test(), so boxing or expensive logic can matter. Common edge cases are null input, forgetting that the lambda can throw its own exception, and assuming Java 8 has Predicate.not — it does not; that helper appears in Java 11, so in Java 8 you use negate(). In streams, keep predicates stateless and non-interfering, especially if the stream is parallel.
Real-World Story: In a checkout service, a team used Predicate<Order> rules to decide whether a promo code could be applied: the customer had to be active, the cart total had to pass a threshold, and the account had to pass fraud checks. During a refactor, one and was accidentally changed to or, so orders that should have failed one rule still slipped through. The result was a spike in discount abuse, support tickets from finance, and logs that showed far more orders marked eligible than normal. The lesson: with predicates, the logic is compact, so a tiny boolean mistake can create a very real business outage.
import java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\npublic class PredicateInterfaceDemo {\n static class Customer {\n private final String name;\n private final int age;\n private final boolean active;\n private final boolean premium;\n private final String email;\n\n Customer(String name, int age, boolean active, boolean premium, String email) {\n this.name = name;\n this.age = age;\n this.active = active;\n this.premium = premium;\n this.email = email;\n }\n\n String getName() {\n return name;\n }\n\n int getAge() {\n return age;\n }\n\n boolean isActive() {\n return active;\n }\n\n boolean isPremium() {\n return premium;\n }\n\n String getEmail() {\n return email;\n }\n\n @Override\n public String toString() {\n return name + "(age=" + age + ", active=" + active + ", premium=" + premium + ", email=" + email + ")";\n }\n }\n\n public static void main(String[] args) {\n List<Customer> customers = Arrays.asList(\n new Customer("Alice", 24, true, false, "alice@example.com"),\n new Customer("Bob", 17, true, true, "bob@example.com"),\n new Customer("Charlie", 31, false, false, null),\n null,\n new Customer("Dora", 29, true, true, "dora.example.com")\n );\n\n // Objects::nonNull is the safe front-door check; everything else should only run after this passes.\n Predicate<Customer> nonNull = Objects::nonNull;\n Predicate<Customer> active = Customer::isActive;\n Predicate<Customer> adult = c -> c.getAge() >= 18;\n Predicate<Customer> hasValidEmail = c -> c.getEmail() != null && c.getEmail().contains("@");\n\n Predicate<Customer> eligible = nonNull.and(active).and(adult).and(hasValidEmail);\n\n List<Customer> eligibleCustomers = customers.stream()\n .filter(eligible)\n .collect(Collectors.toList());\n\n System.out.println("Eligible customers: " + eligibleCustomers);\n\n // negate() flips the yes/no result. In Java 8, this is the standard way to invert a predicate.\n Predicate<Customer> premium = Customer::isPremium;\n Predicate<Customer> regularCustomer = premium.negate();\n System.out.println("Alice is regular customer? " + regularCustomer.test(customers.get(0)));\n\n // isEqual() is handy for exact value checks and is null-safe for equality comparisons.\n Predicate<String> isAlice = Predicate.isEqual("Alice");\n System.out.println("isEqual(\"Alice\").test(\"Alice\") = " + isAlice.test("Alice"));\n System.out.println("isEqual(\"Alice\").test(\"Bob\") = " + isAlice.test("Bob"));\n\n // Edge case: a predicate that blindly dereferences fields will fail on null input.\n Predicate<Customer> unsafeNameCheck = c -> c.getName().length() > 3;\n try {\n System.out.println("Unsafe check on null customer = " + unsafeNameCheck.test(customers.get(3)));\n } catch (Exception e) {\n System.out.println("Expected failure on null input: " + e.getClass().getSimpleName());\n }\n\n // Short-circuit demo: the right side is skipped because the left side is already false.\n AtomicBoolean expensiveCalled = new AtomicBoolean(false);\n Predicate<Customer> expensiveCheck = c -> {\n expensiveCalled.set(true);\n return c.getName().length() > 3;\n };\n Predicate<Customer> alwaysFalse = c -> false;\n\n boolean result = alwaysFalse.and(expensiveCheck).test(customers.get(0));\n System.out.println("Short-circuit result = " + result + ", expensive check called = " + expensiveCalled.get());\n }\n}Follow-up & Tricky Questions:
Predicate a functional interface? Because it has exactly one abstract method, test(T), so a lambda can stand in for the implementation.Predicate different from Function<T, Boolean>? Predicate is the right semantic type for yes/no checks, and it also gives you built-in composition methods like and, or, and negate.and do if the first predicate is false? It short-circuits, so the second predicate is not evaluated. That matters for performance and for avoiding exceptions in the right-hand predicate.IntPredicate instead of Predicate<Integer>? Use IntPredicate when you are working with primitive ints in hot code or primitive streams, because it avoids boxing and unboxing overhead.Predicate change outside state? It can, but that is usually a bad idea. Predicates are easiest to reason about when they are stateless and only return true or false based on their input.Predicate.isEqual(null) valid? Yes. It creates a predicate that matches only null values, because it uses standard equality rules under the hood.Predicate throw a checked exception? Not directly. You must catch it inside the lambda or wrap it in an unchecked exception, because test() does not declare checked exceptions.Predicate.not exist in Java 8? No. In Java 8 you use negate(); Predicate.not(...) arrives later in Java 11.and always evaluate both sides? No, that is the trap. It behaves like boolean &&, so it skips the right side when the left side is already false.Predicate always safe in parallel streams? Only if it is stateless and thread-safe. If it touches shared mutable data, parallel execution can produce races and inconsistent results.Function<T, Boolean> when you really need a predicate. Correction: use Predicate<T>; it reads better and gives you and, or, and negate.Objects::nonNull or explicit null checks before dereferencing fields.and or or always run. Correction: they short-circuit, so order matters for performance and safety.IntPredicate when boxing cost matters.Memory Hook: Think of a Predicate as a bouncer at one door: each person gets a simple yes/no stamp, and and, or, and negate are the bouncer's rulebook.
Predicate<T> = one input, boolean output.test(T).java.util.function.and, or, negate.Predicate<String> that checks whether a password has at least 8 characters.if block into a readable predicate chain and use it in a stream filter.