Zum Hauptinhalt springen

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:

  1. What is a functional interface?
  2. What are the four core types in java.util.function?
  3. How do lambdas and method references relate?
  4. What can a lambda capture?
  5. 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

TypeShapeExample
Predicate<T>T → booleanorder -> order.isOpen()
Function<T,R>T → ROrder::id
Consumer<T>T → voidlog::info
Supplier<T>() → TOrderNotFoundException::new

Relatives I also meet:

  • UnaryOperator<T> is Function<T,T>
  • BiFunction<T,U,R>, BiPredicate, BiConsumer
  • Comparator<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

FormMeaning
Type::staticMethodx -> Type.staticMethod(x)
instance::methodx -> instance.method(x)
Type::instanceMethodx -> 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 a Function-shaped bean.
  • ApplicationListener<E> / Consumer<ApplicationEvent>.
  • orElseThrow(OrderNotFoundException::new) in services (Week 4–5).
  • @Bean methods often return functional types: Clock, PasswordEncoder as a method ref in older examples, HandlerFilterFunction in WebFlux.
  • Constructor injection of PricingPolicy is still composition. The implementation may be a lambda in a @Bean method 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 Functionapply 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/orElseGet are Predicate, Function, Consumer, Supplier.

Captured locals are effectively final. Extract a method when the lambda grows.

Next: Week 7 Day 2 — Stream Pipelines and Laziness