1. 基础语法入门

1.1. 规则

先用最直观的例子理解转化逻辑。

// 传统写法
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
}).start();

// Lambda写法
new Thread(() -> System.out.println("Hello")).start();

规则总结:

  • Lambda 必须基于函数式接口(只有一个抽象方法的接口)

  • 语法结构
    (参数列表)-> {函数体}

  • 参数类型可以省略;函数体若只有一句可省略大括号和 return。

1.2. 示例

1.2.1. 无 Lambda 版本(老写法)

interface Demo {
    void run(String name);
}

public class LambdaTest {
    public static void main(String[] args) {
        Demo d = new Demo() {
            @Override
            public void run(String name) {
                System.out.println("Hello, " + name);
            }
        };

        d.run("毛凯翔");
    }
}

这段代码没问题,但很啰嗦 —— 创建匿名类、写接口名、重写方法。

1.2.2. 改成 Lambda 写法(带参数)

只要接口里只有一个方法,就可以用 Lambda 简写:

interface Demo {
    void run(String name);
}

public class LambdaTest {
    public static void main(String[] args) {
        // 用Lambda写法,直接实现接口方法
        Demo d = (name) -> {
            System.out.println("Hello, " + name);
        };

        d.run("毛凯翔");
    }
}

部分

含义

(name)

代表接口方法的参数列表

->

Lambda 的核心符号,读作“去执行”

{ ... }

方法体

进一步简化(语法糖)

Demo d = name -> System.out.println("Hello, " + name);

省了:

  • 参数的小括号(因为只有一个参数)

  • 花括号(因为只有一句话)

  • return(因为只有一行返回表达式)

1.2.3. 多参数的 Lambda 示例

比如写一个“计算器”:

interface Calculator {
    int add(int a, int b);
}

public class LambdaTest {
    public static void main(String[] args) {
        Calculator c = (a, b) -> a + b;

        int sum = c.add(5, 7);
        System.out.println(sum); // 输出 12
    }
}

(a, b) 是参数列表,-> 后面是返回表达式。

1.2.4. 函数体有多行代码时

如果逻辑多于一行,必须加 {},并手动写 return

interface Calculator {
    int add(int a, int b);
}

public class LambdaTest {
    public static void main(String[] args) {
        Calculator c = (a, b) -> {
            System.out.println("正在计算...");
            return a + b;
        };

        System.out.println(c.add(3, 4));
    }
}

1.2.5. 带参数的实际使用场景:集合操作

import java.util.*;

public class LambdaTest {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Tom", "Jerry", "Mao");

        // forEach里每个元素都作为参数传入Lambda
        list.forEach(name -> System.out.println("Hello, " + name));
    }
}

在这里:

  • forEach 会遍历每个元素;

  • 每次都会把元素作为参数传给 (name)

  • 于是执行 System.out.println(...)

1.2.6. 带返回值的集合操作

import java.util.*;
import java.util.stream.Collectors;

public class LambdaTest {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 用Lambda转换每个元素
        List<Integer> doubled = numbers.stream()
                .map(x -> x * 2) // 带参数x,返回 x*2
                .collect(Collectors.toList());

        System.out.println(doubled); // [2, 4, 6, 8, 10]
    }
}

1.2.7. 加深理解

Lambda 永远不会自己执行(重点):

示例 1 ~ 4 中,lambda 表达式都不会执行,相当于只是定义了方法,并没被调用。需要在后面手动调用一句。但示例 5 ~ 6 中,集合里写的 lambda 表达式可以直接执行,为什么?

因为集合调用了它。

看一个 forEach 示例

List<String> list = Arrays.asList("Tom", "Jerry", "Mao");

list.forEach(name -> System.out.println("Hello " + name));

你以为 Lambda 自动执行了?其实不是,forEach 内部写了循环代码,会主动去调用这个 Lambda

展开看底层逻辑(伪代码)

forEach 方法的真实实现差不多像这样👇

public void forEach(Consumer<? super T> action) {
    for (T t : this) {
        action.accept(t);  // 这里才是执行 Lambda 的地方
    }
}

所以当你写:

list.forEach(name -> System.out.println("Hello " + name));

时其实等价于:

Consumer<String> action = (name) -> System.out.println("Hello " + name);
for (String t : list) {
    action.accept(t);  // 每个元素执行一次 Lambda
}

✅ Lambda 并不是“自动运行”,而是被 forEach 这种方法主动调用了。

2. 设计思想与方法

3. 函数式接口体系

4. Stream

5. 高阶技巧

6. 底层原理

7. 实战练习