Functional Java basics: interview questions & scenarios
These are the lambda and functional-style questions I ask most in Java backend interviews. Half of them are short code puzzles, because a three-line lambda that does not compile, or a stream that prints nothing, tells me quickly whether you understand capture and laziness or have only memorised syntax.
Here's how to use this page: read the question, say your answer out loud first, then open the model answer. The "What if..." follow-ups are the ones I'd ask next, so don't skip them. If a question stumps you, the lesson behind it is in this topic: functional interfaces, callbacks and functional programming.
What is a functional interface? Is Comparator one, even though it declares two abstract-looking methods?
Tests single abstract method, @FunctionalInterface, Object methods
Show answerHide answer
Model answer
A functional interface has exactly one abstract method, and that is the method a lambda or method reference implements. default and static methods do not count because they have bodies, and neither do abstract re-declarations of public Object methods such as equals or toString, since every implementation inherits those from Object anyway (JLS 9.8).
Comparator declares int compare(T, T) and boolean equals(Object). Only compare counts, so Comparator is functional. @FunctionalInterface is optional: it makes the compiler reject the interface if it ever gains a second abstract method, and it documents intent.
How I’d say it in the interview
"One abstract method. Defaults, statics and redeclared Object methods do not count, so Comparator qualifies: its equals is inherited from Object by every implementation. The annotation is a compile-time guard, not a runtime feature."
Signals depth
Naming the Object-method exclusion, and knowing that the annotation has no runtime effect.
Red flags
"An interface with one method" (forgets defaults), or "it must be annotated with @FunctionalInterface".
Follow-ups interviewers ask next
What if I add a second abstract method to an interface annotated
@FunctionalInterface?The interface itself stops compiling: "Unexpected @FunctionalInterface annotation ... multiple non-overriding abstract methods found". Without the annotation, the interface compiles and every lambda that targets it breaks instead, often far from the change.
What if the interface extends two parents that each declare
void run()?It's still functional. Both inherited methods have the same signature, so they merge into one abstract method. If the signatures differed, it would have two and stop being functional.
Why doesn't this compile, and how would you fix it?
Tests effectively final, lambda capture
Show answerHide answer
Model answer
long total = 0;
orders.forEach(o -> total += o.cents());
// error: local variables referenced from a lambda expression must be final or effectively final
A lambda captures the value of a local variable, not the variable itself. Locals live in the method's stack frame, and the lambda may outlive that frame or run on another thread, so Java copies the value into the lambda and forbids changing the variable on either side. total += ... modifies it, so it is not effectively final.
The fix is to stop mutating and let the library compute the result: long total = orders.stream().mapToLong(Order::cents).sum();.
How I’d say it in the interview
"Lambdas capture locals by value, so the local must be effectively final, and += breaks that. I would not fight it with a holder; I'd write it as a reduction: mapToLong then sum."
Signals depth
Explaining why the rule exists (copied values, stack frames, thread safety) and reaching for sum() instead of a workaround.
Red flags
"Wrap it in an int[]" as the first and only answer, or "lambdas cannot use local variables".
Follow-ups interviewers ask next
What if I use an
AtomicLongor along[] holder = {0}instead?It compiles, because the reference stays effectively final and you mutate the object it points to. With
forEachon a sequential list it works. But thelong[]version is a data race underparallelStream(), and both versions hide intent. Use them only when the side effect is genuinely the point, such as counting events in a listener.What if
totalwere an instance field instead of a local?Then it compiles. The lambda captures
thisand readsthis.totalon each call; fields have no effectively-final rule. It is still not thread-safe, and it now ties the lambda's lifetime to the whole object.
Which of these lambdas compile: A, B or C?
Tests effectively final, enhanced for, reassignment after capture
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
What does this stream print?
Tests stream laziness, vertical evaluation, short-circuiting
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
What does this print, and what's the difference between orElse and orElseGet?
Tests eager vs lazy arguments, Supplier
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
Name the four kinds of method reference. What's the difference between order::totalCents and Order::totalCents?
Tests bound vs unbound receivers, eager evaluation
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
Your checkout service started timing out after a refactor from synchronous code to CompletableFuture.thenApply. Walk me through which thread runs the callback.
Tests CompletableFuture threading, sync vs async callbacks, production timeouts
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
Your async notification task throws in production, but there is no stack trace in the logs. Why can that happen with ExecutorService.submit?
Tests async error handling, Future, uncaught exceptions
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
Write a method that takes a list of pricing rules, each a Function<Long, Long>, and returns one function that applies them in order.
Tests higher-order functions, composition, reduce
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
Is this method pure? How would you make it testable?
Tests pure functions, referential transparency, side effects
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
Your order export endpoint became flaky after someone changed stream() to parallelStream() and kept forEach(result::add). Walk me through the bug.
Tests parallel streams, shared mutable state, collectors
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
Would you replace every loop in our codebase with streams? When wouldn't you use one?
Tests imperative vs declarative, performance, side effects, recursion
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
Our service's heap grows by about 1 GB a day. The heap dump shows thousands of UserSession objects whose path to GC root goes through a CopyOnWriteArrayList in PriceFeed. What's happening?
Tests listener leaks, bound method references, lifecycle
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
Is a lambda an anonymous inner class under the hood? What does javac generate?
Tests invokedynamic, LambdaMetafactory, hidden classes, capture
Model answer, how to say it, 2 follow-ups: unlock with the Java Pass
Premium question set
Keep reading with the Java Pass
The rest of this question set is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
From ₹199 · one-time payment · No auto-renewal