Week 7 Day 1 — Functional Interfaces and Lambdas
Goal
Today I want lambdas as one-method types, so a stream pipeline is not magic syntax.
Main questions:
- What is a functional interface?
- What are the four core types in
java.util.function? - How do lambdas and method references relate?
- What can a lambda capture?
- How does Spring already use these types?
1. One abstract method
A functional interface has exactly one abstract method. It may have default and static methods. @FunctionalInterface makes the compiler enforce that.
@FunctionalInterface
public interface PricingPolicy {
Money price(CreateOrderRequest request);
}
A lambda is an instance of that type:
PricingPolicy vip = request -> request.quantity() > 10
? Money.eur(900)
: Money.eur(1000);
This is polymorphism (Week 3) with less ceremony. vip is still an object that implements price.
Memory sentence:
A lambda is an instance of a one-method interface. The compiler infers which method.
2. The four types I name in interviews
| Type | Shape | Example |
|---|---|---|
Predicate<T> | T → boolean | order -> order.isOpen() |
Function<T,R> | T → R | Order::id |
Consumer<T> | T → void | log::info |
Supplier<T> | () → T | OrderNotFoundException::new |
Relatives I also meet:
UnaryOperator<T>isFunction<T,T>BiFunction<T,U,R>,BiPredicate,BiConsumerComparator<T>is a functional interface (compare)- Primitive specializations:
IntPredicate,ToLongFunction— avoid boxing in hot loops
Predicate<Order> open = Order::isOpen;
Function<Order, Long> id = Order::id;
Consumer<Order> audit = this::audit;
Supplier<Clock> clocks = Clock::systemUTC;
Streams are these types on a pipeline: filter takes Predicate, map takes Function, peek/forEach take Consumer, orElseGet takes Supplier.
3. Method references
| Form | Meaning |
|---|---|
Type::staticMethod | x -> Type.staticMethod(x) |
instance::method | x -> instance.method(x) |
Type::instanceMethod | x -> x.method() |
Type::new | () -> new Type() or x -> new Type(x) |
I use a method reference when the lambda would only call that method. I keep a lambda when there is a small expression.
4. Capture and effectively final
A lambda may read locals from the enclosing method. Those locals must be final or effectively final (never reassigned).
int factor = 2;
list.replaceAll(n -> n * factor); // ok
factor = 3; // would make the lambda illegal
The lambda captures the value (or the reference). For objects, it captures the reference: mutating the object is allowed; reassigning the variable is not.
This is the same “copy the reference” idea as pass-by-value (Week 1). A lambda that mutates a list it captured is a side effect. Streams will punish that (Day 5).
5. Spring connection
Converter<S,T>is aFunction-shaped bean.ApplicationListener<E>/Consumer<ApplicationEvent>.orElseThrow(OrderNotFoundException::new)in services (Week 4–5).@Beanmethods often return functional types:Clock,PasswordEncoderas a method ref in older examples,HandlerFilterFunctionin WebFlux.- Constructor injection of
PricingPolicyis still composition. The implementation may be a lambda in a@Beanmethod or a named class.
I still write a named class when the policy has state, logging, or a name I want in stack traces. Lambdas are anonymous: debugging a 40-line lambda is worse than a class.
6. Common traps
Trap 1: “A lambda is a function, not an object.” It is an object of a functional interface type.
Trap 2: Reassigning a captured local.
Trap 3: Checked exceptions in a Function — apply does not declare throws IOException. I wrap (Day 5) or do not use a stream.
Trap 4: Predicate vs Function<T,Boolean> — use Predicate so and/or/negate exist and so I do not box a boolean by habit.
Trap 5: 30-line lambdas as “cleaner than a method.” Extract a method and reference it.
Practice Questions and Answers
Question 1
What is a functional interface?
Answer:
An interface with exactly one abstract method. Lambdas and method references implement that method. @FunctionalInterface is optional documentation the compiler checks.
Question 2
Name the four core java.util.function types.
Answer:
Predicate<T> tests, Function<T,R> transforms, Consumer<T> consumes with a side effect, Supplier<T> provides a value. Stream filter/map/forEach and Optional.orElseGet are those four.
Question 3
Why must captured locals be effectively final?
Answer:
The lambda may run later, on another stack. Java copies the local (or the reference) at creation. Reassignment would make it unclear which value is seen. Mutating an object through a captured reference is still allowed — and is often a side-effect bug.
Question 4
When do I prefer a named class over a lambda?
Answer:
When the behavior has a name, fields, or more than a few lines; when I need a stable stack frame; when Spring should inject collaborators into that policy. Lambdas are for short, obvious mappings.
Question 5
Is Comparator a functional interface?
Answer:
Yes. compare(T,T) is the single abstract method. Comparator.comparing(Order::createdAt) is a factory that returns one. default methods like thenComparing do not count against the one-method rule.
Memory sentences
A lambda is an instance of a one-method interface.
filter/map/forEach/orElseGetare Predicate, Function, Consumer, Supplier.
Captured locals are effectively final. Extract a method when the lambda grows.