一、先给结论:Lambda 是什么?它解决了什么问题?

Lambda 表达式用于简化匿名内部类对函数式接口的实现方式。

在 Java 中,只要一个接口中有且仅有一个抽象方法,它就可以作为 Lambda 的目标类型,这类接口被称为:

函数式接口(Functional Interface)

@FunctionalInterface
public interface Runnable {
    void run();
}

@FunctionalInterface 注解不是强制的,但非常推荐使用,因为它可以:

  1. 编译期校验接口是否真的只有一个抽象方法

  2. 明确表达:这个接口就是为 Lambda 准备的

一句话先记住:

👉 Lambda 的目的不是炫技,而是让“行为”的表达更简单。


二、如果没有 Lambda,我们以前是怎么写代码的?

在 Java 8 之前,Java 是不能直接传递一段“要做的事情”的

如果你想把一段逻辑交给别人执行,只能:

  • 把逻辑写进一个类

  • 再把这个类的对象传递出去

也就是说:

逻辑必须依附在“对象”上存在

而承担这个角色最多的,就是——匿名内部类


三、匿名内部类是什么?(Lambda 出现之前的答案)

匿名内部类:在创建对象的同时,定义类的实现,但这个类没有名字。

它的核心特点只有一句话:

这是一个“真实存在的类”,只是没有名字。


3.1 实现接口的匿名内部类(最常见)

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

这一行代码,其实同时做了三件事:

  1. 定义了一个没有名字的类

  2. 这个类实现了 Runnable 接口

  3. 并立即创建了这个类的对象

因为这个类没有名字,所以称为 匿名内部类


3.2 继承类的匿名内部类

Thread t = new Thread() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

这里并没有显式写:

class Xxx extends Thread { }

而是:

  • 临时继承了 Thread

  • 重写了它的方法

  • 只用一次,用完即弃


3.3 匿名内部类的特点总结

特点 说明
没有类名 无法复用
定义即创建 只能创建一个实例
本质是类 有自己的 this
常用于一次性逻辑 回调、监听器

四、匿名内部类的能力边界

4.1 接口中有多个抽象方法,也可以用匿名内部类吗?

答案是:可以,而且完全合法。

这是匿名内部类无法被 Lambda 替代的重要原因之一


4.2 示例:包含多个抽象方法的接口实现

interface Animal {
    void eat();
    void sleep();
}

使用匿名内部类实现:

Animal animal = new Animal() {
    @Override
    public void eat() {
        System.out.println("动物在吃东西");
    }

    @Override
    public void sleep() {
        System.out.println("动物在睡觉");
    }
};

原因其实很简单:

匿名内部类,本质上是一个“完整的类”。

只要是类,就必须实现接口中定义的所有抽象方法


五、Lambda 是如何简化匿名内部类的?

对比最直观。

匿名内部类:

Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello Lambda");
    }
};

Lambda:

Runnable r2 = () -> System.out.println("Hello Lambda");

变化只有一个:

👉 去掉了所有“与行为无关的结构代码”。


六、Lambda 表达式代码示例(从简单到常见)

6.1 Runnable 示例

匿名内部类写法:

Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello Lambda");
    }
};

Lambda 写法:

Runnable r2 = () -> System.out.println("Hello Lambda");

可以看到:

  • 接口名、方法名全部消失
  • 只剩下“要执行的逻辑”
    这是 Lambda 最直观的价值体现。

6.2 Comparator 示例

假设我们要按照字符串长度排序。

匿名内部类写法:

Comparator<String> comparator1 = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
};

Lambda 写法:

Comparator<String> comparator2 = (a, b) -> a.length() - b.length();

这里:(a, b) 对应方法参数-> 右边是比较逻辑
编译器会根据目标接口,自动推断参数类型。


6.3 Lambda + Stream API(最常见)

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

numbers.stream()
       .filter(n -> n % 2 == 0)
       .map(n -> n * n)
       .forEach(n -> System.out.println(n));

这段代码几乎就是自然语言

过滤 → 转换 → 消费


七、为什么 Lambda 只能用于函数式接口?

因为:

一个 Lambda 表达式,只能承载“一段行为”。

如果接口中有多个抽象方法:

  • 编译器无法判断你这段 Lambda 是给哪个方法的

  • 语义也会变得不清晰

所以 Java 直接规定:

👉 Lambda 只能用于“只有一个抽象方法”的接口。


八、Lambda 与匿名内部类的本质区别

8.1 匿名内部类this 指向的不同(重点)

public class Test {
    void test() {
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println(this);
            }
        };

        Runnable r2 = () -> {
            System.out.println(this);
        };
    }
}

这里的关键点在于:

new Runnable() { ... }

这行代码创建了一个新的类,并立即创建了这个类的一个对象。因此:

  • 匿名内部类是一个“真实存在的类”
  • 它有自己的对象
  • 它自然也有自己的 this

所以在匿名内部类中:

this 指向的就是匿名内部类对象本身(也就是变量 r 所引用的那个对象)。


8.2 Lambda 表达式中的 this

再看使用 Lambda 的写法:

Runnable r = () -> {
    System.out.println(this);
};

这一次,你并没有写 new Xxx(),也没有创建新的类。

这是理解 Lambda 的关键:

Lambda 表达式不会生成新的类或新的对象。

它只是外部类中一段普通的方法逻辑,被“传递”给了函数式接口。因此,Lambda 中:

不存在属于 Lambda 自己的 this

既然 Lambda 没有自己的 this,那么:

Lambda 中的 this,只能指向它所在的外部类对象。

例如:

public class Demo {
    public void test() {
        Runnable r = () -> {
            System.out.println(this);
        };
        r.run();
    }
}

这里的 this,指向的就是 Demo 的实例对象。


8.3 变量捕获(effectively final)

int x = 10;

Runnable r = () -> {
    System.out.println(x);
};

// x = 20; // 编译错误

Lambda 只能使用:

  • final 变量

  • 事实不可变(effectively final)变量

这是为了保证行为一致性和并发安全。


九、什么时候该用 Lambda?什么时候该用匿名内部类?

场景 建议
接口只有一个抽象方法 Lambda
接口有多个抽象方法 匿名内部类
需要字段或状态 匿名内部类
Stream / 集合操作 Lambda
依赖自身 this 匿名内部类

十、全文总结

Lambda:写得少,看得快,表达“要做什么”。
匿名内部类:结构完整,能力更强,是 Lambda 的基础。

最后记住一句就够了:

👉 能用 Lambda 的地方优先用 Lambda;用不了的时候,匿名内部类一定能兜底。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐