Lambda表达式

在Java编程的世界中,Lambda表达式是Java 8中引入的一个重要的新特性。它们提供了一种简洁的方式来表示匿名函数,即没有名称的函数。

Lambda表达式有助于编写更具可读性和可维护性的代码,特别是在处理函数式接口时。

Java8之前,写匿名类非常繁琐,代码冗长,引入Lambda表达式和函数式接口,让Java代码更加简洁。

Lambda表达式是JDK8中引入的语法糖,用于简化匿名类实现函数式接口的代码,写起来简单又高效。

Lambda是一种简洁的语法形式,用于表示匿名函数,没有方法名,没有修饰符

(parameters) -> { body }

参数、箭头、方法体。参数可以省略类型,方法体则是要执行的代码。

函数式接口

函数式接口是只包含一个抽象方法的接口,通常使用**@FunctionalInterface**注解标识。

  • 核心特点:只能有一个抽象方法
  • 作用:作为Lambda表达式的类型
    Lambda表达式本质上是对函数式接口的实现。

三大对应关系

  1. 方法签名对应:Lambda表达式的参数列表、返回类型必须与接口的唯一抽象方法相匹配
  2. 类型自动推断:编译器通过上下文(赋值目标的类型)自动推断Lambda参数和返回值的类型,无需显式声明
  3. 运行时实现:Lambda表达式在编译阶段被转换成函数式接口的具体实现,实现了接口方法
    使用场景:函数式接口主要用于函数式编程、流操作(Stream API)、事件处理等场景,Lambda表达式使这些操作的代码更加简洁优雅
    在这里插入图片描述

如何用Lambda表达式实现匿名函数

JDK8之前,实现一个简单功能往往需要写一个匿名类,例如:

interface Greeting {
    void sayHello();
}

public class Main {
    public static void main(String[] args) {
        Greeting greeting = new Greeting() {
            @Override
            public void sayHello() {
                System.out.println("传统写法:Hello!");
            }
        };
        greeting.sayHello();
    }
}

使用Lambda表达式写法,

interface Greeting {
    void sayHello();
}

public class Main {
    public static void main(String[] args) {
        // 使用Lambda表达式创建 实现函数式接口
        Greeting greeting = () -> System.out.println("Lambda表达式:Hello!");
        greeting.sayHello();
    }
}

Lambda表达式更简洁,直接表达函数实现。

Lambda表达式的几种形式:

  • 无参数,单行语句,() -> System.out.println("Hello"),方法体只有一行代码时,可省略大括号。
  • 单参数,单行语句,x -> System.out.println(x);,只有一个参数时,可省略括号,
  • 多参数,多行语句,(a, b) -> { return a + b; },方法体多行代码时,需要大括号
  • 省略return,(a, b) -> a + b,只有一行返回语句时,可省略return。

示例

计算机效果

例如使用Lambda表达式实现计算机的功能,

// 计算机接口
@FunctionalInterface
interface Calculator {
    // 只有一个方法
    int calculate(int a, int b);
}

public class Main {
    public static void main(String[] args) {
        // 使用Lambda表达式实现函数接口
        Calculator addition = (a, b) -> a + b;
        Calculator multiplication = (a, b) -> a * b;
        // 加法和乘法效果
        System.out.println("加法效果:" + addition.calculate(5, 3));
        System.out.println("乘法效果:" + multiplication.calculate(5, 3));
    }
}


输出:

14:52:14
加法效果:8
14:52:14
乘法效果:15

实现线程任务

使用Runnable接口实现线程任务:

传统重写Runnable接口方法,

package java.lang;

/**
 * Represents an operation that does not return a result.
 *
 * <p> This is a {@linkplain java.util.function functional interface}
 * whose functional method is {@link #run()}.
 *
 * @author  Arthur van Hoff
 * @see     java.util.concurrent.Callable
 * @since   1.0
 */
@FunctionalInterface
public interface Runnable {
    /**
     * Runs this operation.
     */
    void run();
}

重写

public class TestLambda {
    public static void main(String[] args) {
        // 传统重写Runnable接口run方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("传统写法:线程执行中...");
            }
        });
    }
}

可以看见,Runnable是Java中最经典、最基础的函数式接口之一。

判断一个接口是不是函数式接口的唯一标准是:它有且仅有一个抽象方法。
Runnable 接口中只有一个抽象方法:void run();

因此,它完美契合了 Lambda 表达式的替换规则。这也是为什么你可以用 () -> { … } 这种极其简洁的语法来替代以前冗长的匿名内部类去创建线程。这也正是 Java 8 能够用 Lambda 优雅地开启多线程的根本原因。

new Thread(() -> {
            System.out.println("Lambda写法:线程执行中...");
        }).start();

又或者:

new Thread(() -> {
    // thread1 的业务逻辑...
}, "thread1").start();

这里的 () -> { … } 就是在定义这个子线程要干什么活;而后面的 “thread1” 则是给这个线程起了一个名字,方便在控制台打印日志时区分。Thread(() -> {}) 只是把以前又长又丑的“匿名内部类”换成了现代 Java 推荐的、更优雅的 Lambda 语法糖,本质上它们做的事情是一模一样的。

集合遍历与操作

传统方式下,需要写for-each循环或获取迭代器来遍历集合,有了Lambda,配合Stream API,代码变得及其优雅:

List<String> list = Arrays.asList("Alice", "Bob", "Charlie");

// 传统写法
for (String name : list) {
    System.out.println(name);
}

// Lambda写法
list.forEach(name -> System.out.println(name));

// 进阶简写
list.forEach(System.out::println);

事件监听器处理

在 Swing/JavaFX 等图形界面开发中,以前为按钮绑定点击事件需要写一大段匿名内部类,现在只需一行:

JButton button = new JButton("点击我");

// Lambda直接传递点击后的行为逻辑,意图一目了然
button.addActionListener(e -> System.out.println("按钮被触发了!")); 

自定义排序接口

对集合进行复杂排序时,Lambda 能让比较逻辑非常清晰:

List<String> words = Arrays.asList("Java", "Python", "C++");

// 按字符串长度进行升序排序
words.sort((a, b) -> a.length() - b.length());

异步计算与回调

在处理耗时的异步任务时,Lambda让链式调用变得极度流畅:

CompletableFuture.supplyAsync(() -> fetchData()) // 异步去拉取数据
    .thenAccept(result -> process(result));      // 拿到结果后自动处理

方法引用

双冒号::
这是 Lambda 的进一步“终极简写”。如果 Lambda 的方法体仅仅是调用了一个现成的方法,就可以直接用 类名::方法名 替代。
例如:(s) -> System.out.println(s) 可以直接写成 System.out::println

更多推荐