Unravel โบ Java concepts โบ Generics and functional
Lambdas and functional interfaces
A lambda is a short, nameless function you can pass around: x -> x * 2.
Learn these first
At a glance
Instead of writing a whole recipe card and naming it, you hand over a sticky note with the one step ("double it") for the other person to apply wherever they need.
- 1The arrow separates the parameters from the body: (a, b) -> a + bA body with one expression returns it; a block in braces needs return.
- 2The target is a functional interface such as Function<T,R> (one input, one output), Predicate<T> (returns boolean), Consumer<T> (returns nothing) or Supplier<T> (no input, returns a value)The compiler infers the types from the context.
- 3A lambda can read local variables from the surrounding method, but only if they are effectively final (never reassigned)This is why you cannot increment a local counter from inside a lambda.
- A lambda is not a new kind of object with its own this: this inside it means the enclosing class
- Lambdas hide control flow: an exception thrown inside is thrown at the place the lambda is called (for example in the middle of a stream pipeline)
- The compiler turns a lambda into a private synthetic method and uses invokedynamic with LambdaMetafactory to create the interface instance at first use
List<String> names = users.stream().map(u -> u.getName()).toList();Read the full explanation (about 2 min)
A lambda is a short, nameless function you can pass around: x -> x * 2. It works wherever Java expects an interface with exactly one abstract method.
A mental model
Instead of writing a whole recipe card and naming it, you hand over a sticky note with the one step ("double it") for the other person to apply wherever they need.
How it works
The arrow separates the parameters from the body: (a, b) -> a + b. A body with one expression returns it; a block in braces needs return.
The target is a functional interface such as Function<T,R> (one input, one output), Predicate<T> (returns boolean), Consumer<T> (returns nothing) or Supplier<T> (no input, returns a value). The compiler infers the types from the context.
A lambda can read local variables from the surrounding method, but only if they are effectively final (never reassigned). This is why you cannot increment a local counter from inside a lambda.
Going deeper
A lambda is not a new kind of object with its own this: this inside it means the enclosing class. For a named behaviour with its own state, use a class instead.
Lambdas hide control flow: an exception thrown inside is thrown at the place the lambda is called (for example in the middle of a stream pipeline). Checked exceptions cannot be thrown from the standard functional interfaces, so wrap them.
Avoid side effects inside lambdas that run in parallel streams or on other threads; captured mutable state becomes a data race.
Under the hood
The compiler turns a lambda into a private synthetic method and uses invokedynamic with LambdaMetafactory to create the interface instance at first use. Non-capturing lambdas are cached as a single instance.
Common mistakes
- Mutating a captured local variable (compile error) and "fixing" it with a one-element array.
- Doing blocking or stateful work in a stream lambda.
- Putting long logic in a lambda instead of a named method.
How Unravel recognises it
When you paste code, Unravel looks for these signs of lambdas and functional interfaces. Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@FunctionalInterface - Types:
FunctionBiFunctionPredicateConsumerSupplierBiConsumerUnaryOperatorRunnableCallableComparator - Language constructs:
lambda
Part of a learning path
- Modern Java: lambdas and streams ยท previous: Interfaces, inheritance and polymorphism ยท next: Method references
- Reactive Java ยท next: Reactive programming (Reactor Mono and Flux)
Related concepts
Builds on this
Frequently asked questions
What is Lambdas and functional interfaces?
A lambda is a short, nameless function you can pass around: x -> x * 2. It works wherever Java expects an interface with exactly one abstract method.
What are the most common mistakes with Lambdas and functional interfaces?
Mutating a captured local variable (compile error) and "fixing" it with a one-element array. Doing blocking or stateful work in a stream lambda. Putting long logic in a lambda instead of a named method.
How can I see Lambdas and functional interfaces in real code?
Paste your own Java into Unravel and it will point out where lambdas and functional interfaces appears, explain how it works at the detail level you choose, and draw the flow of the code around it.