一、什么是 Lambda 表达式?
Lambda 表达式是 Java 8 引入的核心特性,本质是匿名函数(没有名称的函数),用于简化「只有一个抽象方法的接口」(函数式接口)的实现。
- 核心作用:替代匿名内部类,让代码更简洁、更易读。
- 核心思想:强调「做什么」,而非「怎么做」(函数式编程思想)。
1.1 先看对比:匿名内部类 vs Lambda 表达式
以 Runnable 接口(只有一个 run() 抽象方法)为例:
// 1. 传统匿名内部类
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类执行");
}
};
// 2. Lambda 表达式(简化后)
Runnable runnable2 = () -> System.out.println("Lambda 表达式执行");
可见:Lambda 表达式去掉了冗余的模板代码,只保留了核心的执行逻辑。
二、Lambda 表达式的核心语法
2.1 语法结构
Lambda 表达式的基本格式
(参数列表) -> { 方法体 }
各部分说明:
| 组成部分 | 说明 |
|---|---|
(参数列表) | 可选:参数类型可省略(编译器自动推断);单个参数可省略括号;无参数用 () |
-> | 必选:分隔符,连接参数列表和方法体 |
{ 方法体 } | 可选:单行代码可省略 {} 和 ;;有返回值时单行代码可省略 return |
2.2 语法示例(以 Comparator 接口为例)
Comparator<Integer> 接口只有一个 compare(T o1, T o2) 抽象方法,用于比较两个值:
import java.util.Comparator;
public class LambdaSyntaxDemo {
public static void main(String[] args) {
// 1. 完整写法(参数类型+括号+方法体+return+{})
Comparator<Integer> comparator1 = (Integer a, Integer b) -> {
return a - b;
};
// 2. 简化:省略参数类型(编译器自动推断)
Comparator<Integer> comparator2 = (a, b) -> {
return a - b;
};
// 3. 简化:单行方法体省略 {} 和 return
Comparator<Integer> comparator3 = (a, b) -> a - b;
// 4. 测试
System.out.println(comparator3.compare(5, 3)); // 输出:2
}
}
2.3 不同场景的语法简化
| 场景 | Lambda 写法示例 | 说明 |
|---|---|---|
| 无参数、无返回值 | () -> System.out.println("Hello") | 无参数用 () |
| 单个参数、无返回值 | s -> System.out.println(s) | 单个参数可省略括号 |
| 多个参数、有返回值 | (a, b) -> a + b | 单行返回省略 return |
| 多行方法体 | (a, b) -> { int sum = a + b; return sum; } | 多行必须加 {} 和 return |
三、函数式接口(Lambda 的前提)
Lambda 表达式只能用于函数式接口,这是核心前提!
3.1 什么是函数式接口?
- 定义:只有一个抽象方法的接口(可以有默认方法、静态方法)。
- 标识:可通过
@FunctionalInterface注解标记(可选,但推荐,编译器会检查是否符合函数式接口规范)。
3.2 常见内置函数式接口(Java 8 提供)
Java 8 在 java.util.function 包下提供了大量常用函数式接口,无需自定义:
| 接口名 | 抽象方法 | 用途 | 示例 |
|---|---|---|---|
Consumer<T> | void accept(T t) | 消费一个参数,无返回值 | (s) -> System.out.println(s) |
Supplier<T> | T get() | 无参数,返回一个值 | () -> new ArrayList<>() |
Function<T, R> | R apply(T t) | 接收 T 类型,返回 R 类型 | (s) -> s.length() |
Predicate<T> | boolean test(T t) | 接收 T 类型,返回布尔值 | (n) -> n > 0 |
示例:使用内置函数式接口
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class FunctionalInterfaceDemo {
public static void main(String[] args) {
// 1. Consumer:消费字符串
Consumer<String> printConsumer = s -> System.out.println("消费:" + s);
printConsumer.accept("Hello Lambda"); // 输出:消费:Hello Lambda
// 2. Supplier:生成随机数
Supplier<Double> randomSupplier = () -> Math.random();
System.out.println("生成随机数:" + randomSupplier.get()); // 输出:生成随机数:0.xxxx
// 3. Function:将字符串转为长度
Function<String, Integer> lengthFunction = s -> s.length();
System.out.println("字符串长度:" + lengthFunction.apply("Java")); // 输出:字符串长度:4
// 4. Predicate:判断是否为偶数
Predicate<Integer> evenPredicate = n -> n % 2 == 0;
System.out.println("是否为偶数:" + evenPredicate.test(6)); // 输出:是否为偶数:true
}
}
3.3 自定义函数式接口
如果内置接口不满足需求,可自定义函数式接口:
// 自定义函数式接口(@FunctionalInterface 注解可选,但推荐)
@FunctionalInterface
interface Calculator {
int calculate(int a, int b); // 唯一抽象方法
// 允许有默认方法(Java 8 新增)
default void printTip() {
System.out.println("这是一个计算器接口");
}
// 允许有静态方法
static int add(int a, int b) {
return a + b;
}
}
// 使用
public class CustomFunctionalInterfaceDemo {
public static void main(String[] args) {
// Lambda 实现加法
Calculator addCalculator = (a, b) -> a + b;
System.out.println(addCalculator.calculate(3, 5)); // 输出:8
// Lambda 实现乘法
Calculator multiplyCalculator = (a, b) -> a * b;
System.out.println(multiplyCalculator.calculate(3, 5)); // 输出:15
// 调用默认方法
addCalculator.printTip(); // 输出:这是一个计算器接口
}
}
四、Lambda 表达式的常用场景
4.1 集合遍历(forEach)
Java 8 为 Collection 接口新增了 forEach(Consumer) 方法,结合 Lambda 简化遍历:
import java.util.ArrayList;
import java.util.List;
public class LambdaForEachDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Go");
// 传统 for 循环
for (String s : list) {
System.out.println(s);
}
// Lambda + forEach
list.forEach(s -> System.out.println(s));
// 更简化:方法引用(后续补充)
list.forEach(System.out::println);
}
}
4.2 集合排序(sort)
简化 Collections.sort() 或 List.sort() 的排序逻辑:
import java.util.ArrayList;
import java.util.List;
public class LambdaSortDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Banana");
list.add("Apple");
list.add("Cherry");
// 传统排序(匿名内部类)
/*
list.sort(new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
*/
// Lambda 排序(按字符串自然顺序)
list.sort((s1, s2) -> s1.compareTo(s2));
// 输出排序结果
list.forEach(System.out::println); // Apple, Banana, Cherry
}
}
4.3 线程创建(Runnable)
简化线程创建的代码:
public class LambdaThreadDemo {
public static void main(String[] args) {
// 传统匿名内部类创建线程
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程1执行");
}
});
// Lambda 创建线程
Thread thread2 = new Thread(() -> System.out.println("线程2执行"));
thread1.start();
thread2.start();
}
}
五、Lambda 表达式的高级特性
5.1 变量捕获
Lambda 表达式可以访问外部变量,但有严格限制:
- 可以访问静态变量(全局);
- 可以访问成员变量(当前对象的);
- 可以访问局部变量,但局部变量必须是
final或「事实上的 final」(即没有被重新赋值)。
示例:变量捕获
public class LambdaVariableDemo {
// 静态变量
private static String staticVar = "静态变量";
// 成员变量
private String memberVar = "成员变量";
public void test() {
// 局部变量(事实上的 final,未重新赋值)
String localVar = "局部变量";
// Lambda 访问各类变量
Runnable runnable = () -> {
System.out.println(staticVar); // 允许
System.out.println(memberVar); // 允许
System.out.println(localVar); // 允许
// localVar = "修改局部变量"; // 报错:局部变量必须是 final 或事实上的 final
};
runnable.run();
}
public static void main(String[] args) {
new LambdaVariableDemo().test();
}
}
5.2 方法引用(Lambda 的简化版)
当 Lambda 表达式的逻辑只是「调用一个已存在的方法」时,可使用方法引用进一步简化,格式为 类名/对象名::方法名。
常见方法引用类型
| 类型 | 示例 | 等价的 Lambda 表达式 |
|---|---|---|
| 静态方法引用 | Integer::parseInt | s -> Integer.parseInt(s) |
| 实例方法引用 | System.out::println | s -> System.out.println(s) |
| 对象方法引用 | String::length | s -> s.length() |
| 构造方法引用 | ArrayList::new | () -> new ArrayList<>() |
示例:方法引用
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodReferenceDemo {
public static void main(String[] args) {
// 1. 静态方法引用:Integer.parseInt(String)
Function<String, Integer> function1 = Integer::parseInt;
System.out.println(function1.apply("123")); // 输出:123
// 2. 实例方法引用:System.out.println(String)
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.forEach(System.out::println); // 等价于 s -> System.out.println(s)
// 3. 构造方法引用:ArrayList::new
Supplier<List<String>> supplier = ArrayList::new;
List<String> newList = supplier.get();
newList.add("C");
System.out.println(newList); // 输出:[C]
}
}
六、Lambda 表达式的优缺点
优点
- 代码简洁:去掉匿名内部类的冗余模板,核心逻辑更突出;
- 函数式编程:强调「做什么」,符合现代编程思想;
- 便于并行处理:结合 Stream API(Java 8 新增)可轻松实现集合的并行操作。
缺点
- 可读性:过于简化的 Lambda 可能让新手难以理解;
- 调试困难:匿名函数没有名称,调试时栈轨迹不直观;
- 适用场景有限:仅能用于函数式接口,无法替代多方法接口的实现。
总结
- 核心前提:Lambda 表达式仅适用于函数式接口(只有一个抽象方法的接口),
@FunctionalInterface注解可标记并校验。 - 核心语法:
(参数列表) -> { 方法体 },支持多维度简化(省略参数类型、括号、{}、return)。 - 核心价值:简化匿名内部类的写法,结合 Stream API、集合遍历 / 排序等场景能大幅提升代码简洁性;方法引用是 Lambda 的进一步简化形式。
- 注意事项:Lambda 访问局部变量时,变量需是
final或「事实上的 final」;调试难度略高,需适度使用。
1071

被折叠的 条评论
为什么被折叠?



