外观
【Lambda】函数式编程
约 1223 字大约 4 分钟
2025-10-16
什么是Lambda
Lambda的本质是一种匿名函数,比如,在Python里面,要对一个列表按照元素的某个特定属性进行排序,使用Lambda表达式可以非常简洁。 假设有一个包含字典的列表,每个字典表示一个学生,包含“name”和“score”两个键值对,现在要根据学生的分数对列表进行排序,示例代码如下:
students = [{'name': 'Alice','score': 85}, {'name': 'Bob','score': 78}, {'name': 'Charlie','score': 92}]
sorted_students = sorted(students, key=lambda student: student['score'])
print(sorted_students)这段代码中,student: student['score']就是一个Lambda表达式,定义排序规则,根据学生的 “score” 进行排序。对比传统的代码实现,需要先定义一个命名函数来实现排序逻辑,Lambda表达式更加简洁明了,直接在需要的地方嵌入排序逻辑,不需要额外的函数定义。
Java中如何体现
传统代码实现
在java8之前,如果要将接口实现参数化,只能通过匿名内部类。
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("传统方式");
}
};Lambda实现
Java 8 引入的 Lambda 表达式后,将接口实现代码直接作为参数传递。
Runnable r2 = () -> System.out.println("Lambda方式");代码量减少60%,关键是逻辑更加清晰简洁。
函数式接口
问题来了,那是不是所有的接口,都可以将其接口方法实现作为参数使用Lambada进行传递,答案是否。只有能够作为参数传递的接口称作函数式接口。
Lambda 要求函数式接口是单抽象方法接口,什么意思呢?就是这个接口只有一个抽象方法。以上面的Runnable接口为例,我们看看其接口在jdk中的定义:
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();
}从jkd中的定义,除了这个接口只有一个抽象方法之外,发现这个接口还有一个@FunctionalInterface 注解,这个注解的作用就是定义和限制,假设你的接口有两个抽象方法,如果加上这个注解,就会编译不通过,因为不符合函数式接口的规范。
Java内置函数式接口
Java内置了 java.util.function 包提供标准接口。下面列举几个编程中常用的。
Consumer<T>:消费型接口,没有返回值,仅仅对传入的参数进行处理,如list.forEach(System.out::println),会对列表中的每个元素执行打印操作。Function<T, R>:转换型接口,接收一个类型为T的参数,并返回类型为R的结果,如Function<String, Integer> len = s -> s.length();,用于将字符串转换为其长度。Predicate<T>:判断型接口,接收一个参数并返回布尔值,用于条件判断和过滤操作,如list.stream().filter(s -> s.startsWith("A")),筛选出以 “A” 开头的元素。Supplier<T>:返回型接口,没有参数传入, 但是能够返回具体的结果,常用于生产提供类场景。
Lambda语法范式
表达式体
语法范式:(parameters) -> expression
参考代码:(a, b) -> a + b
语句体
语法范式: (parameters) -> { statements; }
参考代码:(a, b) -> { int sum = a + b; return sum; }
语法小技巧
(String a, String b) -> a.compareTo(b)可简写为(a, b) -> a.compareTo(b),减少冗余的类型声明,使代码更加简洁,得益于编译器自动推导参数类型。- 只有一个参数的时候,参数括号可以省略,
(x) -> x*2等价于x -> x*2。
函数式编程带来的问题
调试难
当Lambda表达式出现异常时,调试信息不直观,不能快速定位问题所在。例如,在一个复杂的Lambda表达式中进行数据库查询操作,如果出现SQL语法错误,堆栈跟踪信息可能只显示lambda,无法明确告知或者展示具体的错误位置。
可读性陷进
如果代码超过3行,需谨慎使用Lambda,逻辑变得复杂时,过多的Lambda嵌套或过长的表达式会使代码可读性急剧下降,避免省掉一行代码,Debug三天。
闭包限制
除了final修饰的外部变量,其与外部变量无法在Lambda表达式中访问,会引发编译错误。即在Lambda表达式中,不能随意修改外部作用域中的变量,例如:
int num = 10;
Runnable r = () -> {
// num++; // 编译错误
System.out.println(num);
};Lambda可以使降低代码的同时也使代码更加简洁,特别适合那些临时、轻量的逻辑处理场景,让开发者能够更聚焦于业务逻辑本身,而不是繁琐的函数定义。