Логотип Workflow

Article

Functional Interfaces And Lambdas

Topic 11. Functional Interfaces and Lambdas

Functional interfaces and lambdas in Java let you pass behavior as a value. They do not replace OOP; they complement it by making strategy, callback, and transformation logic more concise. For beginners, the main benefit is less boilerplate and clearer behavioral contracts.

Lambda and Functional Interface

1. Core SAM Concept

A functional interface has exactly one abstract method (SAM).

@FunctionalInterface
interface MathOp {
    int apply(int a, int b);
}

MathOp add = (a, b) -> a + b;
System.out.println(add.apply(2, 3)); // 5

@FunctionalInterface is optional but useful: compiler enforces SAM structure.

2. Where It Appears in Real Java

  1. Collections API: sort, removeIf, forEach.
  2. Stream API: map, filter, reduce.
  3. CompletableFuture chains.
  4. Strategy and callback patterns.
  5. Runnable, Callable, executors.

3. Standard Functional Interfaces

InterfaceContract
Predicate<T>T -> boolean
Function<T, R>T -> R
Consumer<T>T -> void
Supplier<T>() -> T
UnaryOperator<T>T -> T
BinaryOperator<T>(T, T) -> T
BiFunction<T, U, R>(T, U) -> R

Example:

Predicate<String> longName = s -> s.length() >= 4;
Function<String, String> upper = String::toUpperCase;
Consumer<String> printer = System.out::println;

List.of("ann", "alex").stream()
        .filter(longName)
        .map(upper)
        .forEach(printer);

4. Lambda Syntax Patterns

() -> 42
x -> x * 2
(a, b) -> a + b
(String s) -> s.trim()
(a, b) -> {
    int r = a + b;
    return r;
}

Readability rule: if lambda gets long, extract a named method.

5. Method References

When lambda only delegates to an existing method, method reference is cleaner:

List<String> names = new ArrayList<>(List.of("Bob", "Ann", "alex"));
names.sort(String::compareToIgnoreCase);

Forms:

  1. ClassName::staticMethod
  2. obj::instanceMethod
  3. ClassName::instanceMethod
  4. ClassName::new

6. Variable Capture and Effectively Final

Lambdas may capture local variables only if they are final or effectively final:

int min = 3;
Predicate<String> p = s -> s.length() >= min;

Invalid:

int min = 3;
min++; // no longer effectively final

This avoids confusing lifecycle and concurrency behavior.

7. Function and Predicate Composition

Function<String, String> trim = String::trim;
Function<String, String> upper = String::toUpperCase;
Function<String, String> pipeline = trim.andThen(upper);

System.out.println(pipeline.apply("  java  ")); // JAVA
Predicate<String> notBlank = s -> !s.isBlank();
Predicate<String> shortWord = s -> s.length() <= 5;
Predicate<String> rule = notBlank.and(shortWord);

Composition helps build reusable, testable business rules.

8. Practical Patterns

Strategy

interface DiscountStrategy {
    double apply(double amount);
}

double checkout(double amount, DiscountStrategy strategy) {
    return strategy.apply(amount);
}

double result = checkout(1000, a -> a * 0.9);

Callback wrapper

void withLogging(String opName, Runnable action) {
    long start = System.nanoTime();
    try {
        action.run();
    } finally {
        System.out.println(opName + " took " + (System.nanoTime() - start));
    }
}

9. Frequent Mistakes

  1. Long lambdas with dense business logic.
  2. Side effects hidden inside stream chains.
  3. Unclear lambda argument names in non-trivial expressions.
  4. Creating custom interfaces when java.util.function already fits.

10. When a Regular Method Is Better

Extract a named method when:

  1. logic is reused,
  2. lambda becomes too long,
  3. logic needs focused unit tests,
  4. method name communicates intent better than inline expression.

Key Takeaway

Functional interfaces define behavior contracts; lambdas provide compact implementations. The model works best with short focused lambdas, clear naming, and disciplined separation of complex logic into named methods.

Please login to pass quizzes.

Quiz

Check what you learned

Practice

Interactive practice

Complete tasks and check your answer instantly.