Lambdas and Functional Interfaces

Streams · lesson 29 of 42 · 4 min read

The handful of interface shapes every stream op takes, and how a lambda becomes one.

Open this lesson in the learning hub

Key points

  • A lambda is an instance of a functional interface: one abstract method, any number of default ones.
  • filter takes a Predicate, map a Function, forEach a Consumer, generate a Supplier.
  • The shape decides the name: no argument is a Supplier, no return is a Consumer, a boolean return is a Predicate.
  • Primitive flavours avoid boxing: IntPredicate, ToIntFunction, IntUnaryOperator.
  • @FunctionalInterface is optional, but it makes the compiler reject a second abstract method.
  • A lambda may only capture effectively final locals, which is exactly why streams push you toward pure functions.

Example

import java.util.*;
import java.util.function.*;

public class Main {
    public static void main(String[] args) {
        Predicate<String> isLong = s -> s.length() > 3;
        Function<String, Integer> len = String::length;
        Consumer<String> shout = s -> System.out.println("consumer  : " + s.toUpperCase());
        Supplier<String> maker = () -> "fresh";
        UnaryOperator<String> trim = String::trim;
        BiFunction<String, String, String> glue = (a, b) -> a + "-" + b;

        System.out.println("predicate : " + isLong.test("ada") + " " + isLong.test("grace"));
        System.out.println("function  : " + len.apply("streams"));
        System.out.println("supplier  : " + maker.get());
        System.out.println("unary     : [" + trim.apply("  pad  ") + "]");
        System.out.println("bifunction: " + glue.apply("map", "reduce"));
        shout.accept("hello");

        List<String> names = List.of("ada", "grace", "alan");
        System.out.println("in stream : " + names.stream().filter(isLong).map(len).toList());

        // Primitive flavours skip the wrapper entirely.
        IntPredicate even = n -> n % 2 == 0;
        ToIntFunction<String> size = String::length;
        System.out.println("primitive : " + names.stream().mapToInt(size).filter(even).sum());
    }
}

Work out what goes in and what comes back, and the interface names itself.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Streams course, and every lesson in it is listed on the Streams contents page.