Fundamentals 9 min read

Mastering Java Lambda Expressions: From Anonymous Classes to Method References

This guide walks through the evolution from traditional classes to anonymous inner classes and finally to Java 8 lambda expressions, explains lambda syntax, demonstrates all four method‑reference forms, covers variable capture rules, this binding, practical stream chaining, and highlights common pitfalls for Java developers.

CodeNotes
CodeNotes
CodeNotes
Mastering Java Lambda Expressions: From Anonymous Classes to Method References

Introduction

Lambda expressions, introduced in Java 8, bring functional programming capabilities to the Java language.

Evolution from Anonymous Classes

// Step 1: Traditional class implementation
class PrintTask implements Runnable {
    @Override
    public void run() {
        System.out.println("执行任务");
    }
}
new Thread(new PrintTask()).start();

// Step 2: Anonymous inner class (less code)
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("执行任务");
    }
}).start();

// Step 3: Lambda expression (Java 8, most concise)
new Thread(() -> System.out.println("执行任务")).start();

Lambda Syntax

(parameterList) -> expression
(parameterList) -> { statements; }
// No parameters
Runnable r = () -> System.out.println("无参数");

// Single parameter (parentheses optional)
Consumer<String> print = s -> System.out.println(s);

// Multiple parameters
Comparator<Integer> cmp = (a, b) -> a - b;

// Multi‑line body (requires braces and return)
Function<Integer, String> f = n -> {
    if (n > 0) return "正数";
    if (n < 0) return "负数";
    return "零";
};

// Explicit parameter types (usually omitted)
Comparator<String> c = (String a, String b) -> a.compareTo(b);

Method References

When a lambda merely forwards its arguments to an existing method, the :: operator can replace the lambda.

Four Forms of Method References

Static method reference : ClassName::staticMethod – equivalent to x -> ClassName.staticMethod(x) Instance method reference (object) : object::instanceMethod – equivalent to x -> object.instanceMethod(x) Instance method reference (type) : ClassName::instanceMethod – equivalent to (x, y) -> x.instanceMethod(y) Constructor reference : ClassName::new – equivalent to

x -> new ClassName(x)
import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class MethodReferenceDemo {
    public static void main(String[] args) {
        List<String> names = List.of("Charlie", "Alice", "Bob");

        // 1. Static method reference
        Function<String, Integer> parse = Integer::parseInt;
        System.out.println(parse.apply("42")); // 42

        // 2. Instance method reference (specific object)
        String prefix = "Hello, ";
        Function<String, String> concat = prefix::concat;
        System.out.println(concat.apply("Java")); // Hello, Java

        // 3. Instance method reference (type)
        Function<String, String> upper = String::toUpperCase;
        names.stream().map(upper).forEach(System.out::println);

        // 4. Constructor reference
        Function<String, StringBuilder> sbFactory = StringBuilder::new;
        StringBuilder sb = sbFactory.apply("初始内容");
        System.out.println(sb); // 初始内容

        // 5. Common: System.out::println
        names.forEach(System.out::println);
    }
}

Variable Capture

public class LambdaCapture {
    public static void main(String[] args) {
        String prefix = "你好,"; // effectively final
        List<String> names = List.of("张三", "李四");
        names.forEach(name -> System.out.println(prefix + name));
        // 你好,张三
        // 你好,李四
        // ❌ Modifying the captured variable causes a compile error
        // prefix = "Hi,"; // uncommenting this line makes the lambda invalid
    }
}

Note: Lambda can capture only effectively final variables—variables that are never reassigned after initialization.

this in Lambda

public class LambdaThis {
    private String name = "外部类";

    public void test() {
        // In a lambda, this refers to the enclosing class instance
        Runnable r1 = () -> System.out.println(this.name); // 外部类

        // In an anonymous class, this refers to the anonymous class itself
        Runnable r2 = new Runnable() {
            private String name = "匿名类";
            @Override
            public void run() {
                System.out.println(this.name); // 匿名类
            }
        };

        r1.run(); // 外部类
        r2.run(); // 匿名类
    }
}

Practical Chain Example

record Product(String name, String category, double price) {}

public class LambdaChain {
    public static void main(String[] args) {
        List<Product> products = List.of(
            new Product("苹果", "水果", 5.5),
            new Product("香蕉", "水果", 3.0),
            new Product("牛奶", "饮品", 8.0),
            new Product("橙汁", "饮品", 6.5)
        );

        // Filter fruits → sort by price → extract names → join
        String result = products.stream()
            .filter(p -> "水果".equals(p.category()))
            .sorted(Comparator.comparingDouble(Product::price))
            .map(Product::name)
            .collect(Collectors.joining("、"));
        System.out.println("水果(价格升序):" + result); // 水果(价格升序):香蕉、苹果
    }
}

Common Pitfalls

Variable capture : Only effectively final variables can be captured; attempting to modify them causes a compilation error.

this binding : In a lambda, this points to the enclosing class; in an anonymous class, it points to the anonymous class itself.

Method‑reference requirements : The parameter types and count must match the target functional interface for a method reference to be valid.

Return values : Single‑line lambdas implicitly return the expression; multi‑line bodies require an explicit return statement.

Summary

Lambda basics: (parameters) -> expression replaces anonymous inner classes.

Four method‑reference forms simplify lambdas when the body merely forwards arguments.

Only effectively final variables can be captured; this in a lambda refers to the outer class.

Combining lambda, method references, and the Stream API enables concise, expressive data pipelines.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaLambdaFunctional ProgrammingStream APIMethod Reference
CodeNotes
Written by

CodeNotes

Discuss code and AI, and document daily life and personal growth.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.