Java + Spring Boot track

Lesson 18 · Part 5: Modern Java

Lambdas and Functional Interfaces

Lambda is a short form of anonymous function. This is a core grammar that can be found almost everywhere since JDK 8, including collections, streams, and spring. Also…

Part 5: Modern Java3 min readFree lesson

Lambda is a short form of anonymous function. This is a core grammar that can be found almost everywhere since JDK 8, including collections, streams, and spring. Also learn the method references and standard functional interfaces (Function/Predicate/Consumer/Supplier).

Learning Objectives

  • Know the form of lambda expression ((a, b) -> a + b)
  • Know 4 method references (Foo::bar)
  • Uses the core 4 interfaces of java.util.function
  • Know the constraints (effective final) of variables that a lambda can capture

Key concepts

1) lambda expression

Runnable r = () -> System.out.println("hi");
Comparator<String> byLen = (a, b) -> a.length() - b.length();

The type is inferred from the target functional interface.

2) 4 method references

Type Yes
static method Integer::parseInt
instance method (specific object) System.out::println
instance method (class name) String::toUpperCase
constructor ArrayList::new

3) Standard functional interface

interface abstract method Meaning
Function<T, R> R apply(T) T → R conversion
Predicate<T> boolean test(T) T condition check
Consumer<T> void accept(T) T consumption (power, etc.)
Supplier<T> T get() T supplied without parameters
BiFunction<T,U,R> R apply(T, U) Two inputs → one output

4) effective final

int x = 10;
Runnable r = () -> System.out.println(x);
//x = 20;   // Compilation error: Captured variable must be final in nature

Local variables captured inside a lambda (even without the final keyword) must be immutable once set.

See as example

Example 1 — LambdaBasics.java: Various Forms

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class LambdaBasics {
    public static void main(String[] args) {
        Runnable r = () -> System.out.println("hi");
        r.run();

        Comparator<String> byLen = (a, b) -> a.length() - b.length();
        List<String> words = Arrays.asList("ccc", "a", "bb");
        words.sort(byLen);
        System.out.println(words);
    }
}

Execution result

hi
[a, bb, ccc]

Note: If the lambda body is one line, you can omit return and the curly braces.

Example 2 — MethodReference.java: Method reference

import java.util.List;
import java.util.stream.Collectors;

public class MethodReference {
    public static void main(String[] args) {
        List<String> langs = List.of("java", "kotlin", "scala");
        // (s) -> s.toUpperCase()
        List<String> upper = langs.stream()
            .map(String::toUpperCase)
            .collect(Collectors.toList());
        System.out.println(upper);

        // (s) -> System.out.println(s)
        langs.forEach(System.out::println);
    }
}

Execution result

[JAVA, KOTLIN, SCALA]
java
kotlin
scala

Note: If the lambda simply "calls some method", the method reference is shorter and clearer.

Example 3 — FunctionInterfaces.java: 4 standard types

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class FunctionInterfaces {
    public static void main(String[] args) {
        Function<Integer, Integer> square = n -> n * n;
        Predicate<Integer> isEven = n -> n % 2 == 0;
        Consumer<String> printUp = s -> System.out.println(s.toUpperCase());
        Supplier<String> hello = () -> "Hello";

        System.out.println(square.apply(5));
        System.out.println(isEven.test(10));
        printUp.accept("java");
        System.out.println(hello.get());
    }
}

Execution result

25
true
JAVA
Hello

Memo: The interface of a lambda is naturally determined by looking at what it takes and what it returns.

Example 4 — Capture.java: Capturing variables

public class Capture {
    public static void main(String[] args) {
        int x = 10;
        Runnable r = () -> System.out.println("x=" + x);
        r.run();
        //x = 20;   // Compilation error if uncommented

        for (int i = 0; i < 3; i++) {
            final int idx = i;
            Runnable rr = () -> System.out.println("idx=" + idx);
            rr.run();
        }
    }
}

Execution result

x=10
idx=0
idx=1
idx=2

Note: i of for itself is created anew each time, but to capture it inside a lambda, you need to make a final copy of that round.

Common Mistakes

  1. Forcibly write return in a single line lambda (does not need to be present)
  2. Reassignment to capture variable → compilation error
  3. Forgetting exception handling in lambda and conflicting with Function signature (checked exception)
  4. Confusion about whether String::toUpperCase is an instance method or a static method.
  5. Long use of lambda even when method reference is available

Cleanup

  • Lambda is a short implementation of a functional interface.
  • More concise with method references
  • 80% utilization possible by learning only the standard 4 functional interfaces
  • Capture variables must be effective final

Try it yourself

cd 05_Modern_Java/18_Lambda-and_functional_type/src
javac LambdaBasics.java
java LambdaBasics

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/Capture.javaJava
public class Capture {
    public static void main(String[] args) {
        int x = 10;
        Runnable r = () -> System.out.println("x=" + x);
        r.run();

        for (int i = 0; i < 3; i++) {
            final int idx = i;
            Runnable rr = () -> System.out.println("idx=" + idx);
            rr.run();
        }
    }
}
src/FunctionInterfaces.javaJava
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class FunctionInterfaces {
    public static void main(String[] args) {
        Function<Integer, Integer> square = n -> n * n;
        Predicate<Integer> isEven = n -> n % 2 == 0;
        Consumer<String> printUp = s -> System.out.println(s.toUpperCase());
        Supplier<String> hello = () -> "Hello";

        System.out.println(square.apply(5));
        System.out.println(isEven.test(10));
        printUp.accept("java");
        System.out.println(hello.get());
    }
}
src/LambdaBasics.javaJava
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class LambdaBasics {
    public static void main(String[] args) {
        Runnable r = () -> System.out.println("hi");
        r.run();

        Comparator<String> byLen = (a, b) -> a.length() - b.length();
        List<String> words = Arrays.asList("ccc", "a", "bb");
        words.sort(byLen);
        System.out.println(words);
    }
}
src/MethodReference.javaJava
import java.util.List;
import java.util.stream.Collectors;

public class MethodReference {
    public static void main(String[] args) {
        List<String> langs = List.of("java", "kotlin", "scala");
        List<String> upper = langs.stream()
            .map(String::toUpperCase)
            .collect(Collectors.toList());
        System.out.println(upper);

        langs.forEach(System.out::println);
    }
}

Assignment

Problem 1 — Filter by Predicate

  • File name: Homework01.java
  • Core concepts: Predicate, Stream.filter

Requirements

  • In List<Integer> nums = List.of(3, 7, 1, 9, 4, 6, 8)
  • Only even numbers
  • 5 or more
  • Both conditions (even AND 5 or more)
  • Outputs each filtered result.

Expected output

Even numbers: [4, 6, 8]
5 or more: [7, 9, 6, 8]
Even AND 5 or more: [6, 8]

Problem 2 — Sorting by method reference

  • File name: Homework02.java
  • Key concepts: Comparator, method reference

Requirements

  • record Book(String title, int pages) list
  • pages Ascending
  • title Alphabetically
  • Output sorted twice.

Expected output

In ascending order of pages: [Book[title=A, pages=100], Book[title=C, pages=200], Book[title=B, pages=300]]
Title alphabetically: [Book[title=A, pages=100], Book[title=B, pages=300], Book[title=C, pages=200]]

Check the correct answer

After solving it yourself, compare it with the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

Homework01.javaJava
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/** Filter by Predicate.*/
public class Homework01 {
    public static void main(String[] args) {
        List<Integer> nums = List.of(3, 7, 1, 9, 4, 6, 8);
        Predicate<Integer> even = n -> n % 2 == 0;
        Predicate<Integer> geFive = n -> n >= 5;

        System.out.println("Even numbers:" + filter(nums, even));
        System.out.println("5 or more:" + filter(nums, geFive));
        System.out.println("Even AND 5 or more:" + filter(nums, even.and(geFive)));
    }

    static List<Integer> filter(List<Integer> xs, Predicate<Integer> p) {
        return xs.stream().filter(p).collect(Collectors.toList());
    }
}
Homework02.javaJava
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/** Sort by method reference.*/
public class Homework02 {
    record Book(String title, int pages) {}

    public static void main(String[] args) {
        List<Book> books = new ArrayList<>(List.of(
            new Book("B", 300),
            new Book("A", 100),
            new Book("C", 200)
        ));

        books.sort(Comparator.comparingInt(Book::pages));
        System.out.println("In ascending order of pages:" + books);

        books.sort(Comparator.comparing(Book::title));
        System.out.println("Title alphabetically:" + books);
    }
}

Learn this lesson with a mentor

Work through the Java + Spring Boot track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now