Lambdas and Functional Interfaces — Complete Guide
Fast recall
Quick revision
Core answer
@FunctionalInterface documents intent and prevents accidental addition of a second abstract method. A lambda (params) -> body is a compact syntax for implementing a functional interface; target typing determines which interface from context. The five standard interfaces in java.util.function cover most needs: `FunctionIntFunction, ToIntFunction, IntPredicate, etc.) to avoid boxing overhead. Under the hood, lambdas compile to invokedynamic calls that invoke LambdaMetafactory — stateless lambdas are singletons, capturing lambdas allocate per unique capture. Method references (String::toUpperCase) are typically preferred over the equivalent single-method lambda for readability.Whether you know the standard functional interfaces, can use them idiomatically, and understand lambdas are compiled to invokedynamic not anonymous classes
Creating custom single-method interfaces when java.util.function has an equivalent; using Function<T, Void> instead of Consumer<T>; not knowing primitive specializations exist
Explain invokedynamic compilation model; discuss composition via .andThen/.compose; mention primitive specializations save boxing overhead in hot loops
Interview-ready guide
Interview answer
A functional interface is any interface with exactly one abstract method — Runnable, Comparator, Callable, or the ones from java.util.function. The @FunctionalInterface annotation is a compile-time safeguard against accidentally adding a second abstract method.
A lambda is compact syntax for implementing a functional interface: (params) -> body. Target typing infers which interface the lambda implements from the context — the variable type, parameter type, or return type.
The five standard functional interfaces cover most use cases. `FunctionBi- versions for two arguments, and primitive specializations like IntFunction, ToIntFunction, IntPredicate that avoid boxing overhead — critical in hot loops over millions of elements.
Composition is built in via default methods. function.andThen(g) applies function then g. predicate.and(other) combines two predicates. consumer.andThen(next) chains side effects.
Under the hood, lambdas are not compiled to anonymous classes, despite the common misconception. The compiler emits an `invokedynamic` instruction that calls LambdaMetafactory at runtime. The metafactory generates a lightweight class via java.lang.invoke and caches it. Consequences: stateless lambdas are singletons — () -> 42 allocates once ever — and the JIT inlines lambda bodies into the call site, so on a hot path lambdas match direct-call speed.
Effectively final captured variables — locals referenced inside a lambda cannot be reassigned. The restriction exists because lambdas may outlive the enclosing method, and mutable captured state would need synchronization.
The rule I give junior engineers: use method references when the body is just one method call — String::toUpperCase over s -> s.toUpperCase() — and extract to a named method when the body grows past 5 lines. Lambdas make intent clear for one-liners but hurt readability for longer logic.
Lambdas and Functional Interfaces — Complete Guide
Direct Answer
A functional interface is any interface with exactly one abstract method — the annotation @FunctionalInterface documents intent and prevents accidental addition of a second abstract method. A lambda (params) -> body is a compact syntax for implementing a functional interface; target typing determines which interface from context. The five standard interfaces in java.util.function cover most needs: Function<T, R> (T in, R out), Predicate<T> (T in, boolean out), Consumer<T> (T in, void), Supplier<T> (no in, T out), UnaryOperator<T> / BinaryOperator<T> (same-type transformations). Plus primitive specializations (IntFunction, ToIntFunction, IntPredicate, etc.) to avoid boxing overhead. Under the hood, lambdas compile to invokedynamic calls that invoke LambdaMetafactory — stateless lambdas are singletons, capturing lambdas allocate per unique capture. Method references (String::toUpperCase) are typically preferred over the equivalent single-method lambda for readability.
Common Mistakes & Pitfalls
Creating custom single-method interfaces when java.util.function has an equivalent; using Function<T, Void> instead of Consumer<T>; not knowing primitive specializations exist
Check your understanding
Answer these without looking back at the article.
- Why is `IntFunction<R>` needed separately from `Function<Integer, R>`?
- What does 'effectively final' mean and why does it apply to lambdas?
- How does `invokedynamic` differ from `invokevirtual`?
- When would you prefer an anonymous class over a lambda?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.