Lambda 表达式是 Java 8 引入的重要特性,本质是匿名函数(没有名称的函数),用于简化函数式接口的实现,使代码更简洁、紧凑。它特别适合处理集合的遍历、过滤、映射等操作,是函数式编程在 Java 中的核心体现。
一、Lambda 表达式的基本概念
1. 函数式接口(前提)
Lambda 表达式只能用于函数式接口 ------ 即只包含一个抽象方法的接口 (可包含默认方法或静态方法)。Java 内置了大量函数式接口(如 Runnable
、Comparator
、Consumer
等),也可自定义。
// 自定义函数式接口(只有一个抽象方法)
@FunctionalInterface // 注解可校验是否为函数式接口
interface MathOperation {
int operate(int a, int b);
}
2. Lambda 语法结构
Lambda 表达式的语法格式:
(参数列表) -> { 方法体 }
- 参数列表:与函数式接口中抽象方法的参数一致,可省略参数类型(编译器自动推断)。
->
:箭头符号,分隔参数列表和方法体。- 方法体 :实现抽象方法的逻辑,若只有一行代码可省略
{}
和return
(若有返回值)。
二、Lambda 表达式的简化写法
根据场景不同,Lambda 可逐步简化,以下是常见形式:
1. 完整写法(带参数类型和方法体)
MathOperation addition = (int a, int b) -> {
return a + b;
};
2. 省略参数类型(编译器自动推断)
MathOperation subtraction = (a, b) -> {
return a - b;
};
3. 方法体只有一行代码(省略 {}
和 return
)
MathOperation multiplication = (a, b) -> a * b; // 自动返回结果
4. 无参数的情况
// Runnable 接口(无参数,无返回值)
Runnable runnable = () -> System.out.println("Lambda 运行中");
5. 单个参数(可省略参数的括号)
// Consumer 接口(单个参数,无返回值)
Consumer<String> consumer = s -> System.out.println(s);
三、Lambda 表达式的核心应用场景
1. 替代匿名内部类
传统匿名内部类代码冗长,Lambda 可大幅简化:
传统方式(匿名内部类):
// 线程创建
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程运行");
}
}).start();
Lambda 方式:
new Thread(() -> System.out.println("线程运行")).start();
2. 集合遍历与操作
结合 Java 8 新增的 Stream API
,Lambda 可简洁处理集合的遍历、过滤、排序等:
示例 1:遍历集合
List<String> list = Arrays.asList("apple", "banana", "cherry");
// 传统 for 循环
for (String s : list) {
System.out.println(s);
}
// Lambda + forEach
list.forEach(s -> System.out.println(s));
// 更简化(方法引用)
list.forEach(System.out::println);
示例 2:集合排序
List<Integer> numbers = Arrays.asList(3, 1, 2);
// 传统方式(匿名内部类)
Collections.sort(numbers, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2; // 升序
}
});
// Lambda 方式
Collections.sort(numbers, (a, b) -> a - b); // 升序
// 更简化(方法引用)
numbers.sort(Integer::compareTo);
示例 3:过滤与映射(Stream API)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 过滤偶数并计算平方和
int sum = numbers.stream()
.filter(n -> n % 2 == 0) // 过滤偶数
.map(n -> n * n) // 计算平方
.reduce(0, Integer::sum); // 求和
System.out.println(sum); // 输出:2² + 4² + 6² = 4 + 16 + 36 = 56
3. 自定义函数式接口
通过自定义函数式接口,可将 Lambda 作为参数传递,实现灵活的业务逻辑:
// 自定义函数式接口(处理字符串)
@FunctionalInterface
interface StringProcessor {
String process(String s);
}
// 工具方法:接收 Lambda 作为参数
public static String handleString(String s, StringProcessor processor) {
return processor.process(s);
}
// 使用
public static void main(String[] args) {
// 转换为大写
String upper = handleString("hello", s -> s.toUpperCase());
System.out.println(upper); // HELLO
// 反转字符串
String reversed = handleString("hello", s -> new StringBuilder(s).reverse().toString());
System.out.println(reversed); // olleh
}
四、Lambda 表达式的底层原理
Lambda 表达式在编译后不会生成匿名内部类 ,而是通过 invokedynamic
指令动态生成一个函数式接口的实现类,本质是语法糖,但比匿名内部类更高效(减少类文件生成)。
例如,以下 Lambda 表达式:
Runnable r = () -> System.out.println("Lambda");
编译后会生成一个类似以下的实现类(由 JVM 动态创建,不在字节码中显式存在):
class Lambda$1 implements Runnable {
@Override
public void run() {
System.out.println("Lambda");
}
}
五、Lambda 表达式的注意事项
-
访问外部变量 :Lambda 可访问外部的
final
或事实上 final (即未声明为final
但未被修改)的变量。int num = 10; // 事实上 final(未被修改) Runnable r = () -> System.out.println(num); // 合法 // num = 20; // 若修改,编译报错
-
this
关键字 :Lambda 中的this
指向包围它的外部类对象 ,而匿名内部类的this
指向自身实例。 -
方法引用(进一步简化) :若 Lambda 方法体只是调用一个已存在的方法,可使用方法引用 (
::
语法)进一步简化:// Lambda 方式 list.forEach(s -> System.out.println(s)); // 方法引用方式(等价) list.forEach(System.out::println);
常见方法引用类型:
- 静态方法引用:
ClassName::staticMethod
(如Integer::parseInt
)。 - 实例方法引用:
instance::method
(如str::length
)。 - 类的实例方法引用:
ClassName::method
(如String::equals
)。 - 构造方法引用:
ClassName::new
(如ArrayList::new
)。
- 静态方法引用:
总结
Lambda 表达式的核心价值是简化代码 和支持函数式编程,尤其在集合操作和事件处理中优势明显。其使用前提是函数式接口,语法简洁灵活,可根据场景逐步简化。
掌握 Lambda 表达式需要理解:
- 函数式接口是基础。
- 语法格式:
(参数) -> 方法体
。 - 常见应用:替代匿名内部类、集合遍历与 Stream 操作。
- 方法引用是 Lambda 的进一步简化。
Lambda 表达式是 Java 现代化的重要一步,与 Stream API、Optional 等特性结合,能显著提升代码的可读性和开发效率。