通俗易懂!Java Lambda表达式最全讲解,轻松掌握核心概念
随着编程语言的发展,Java 8 引入了 Lambda 表达式这一重要特性,它不仅简化了代码,也为函数式编程的应用提供了便利。Lambda 表达式的核心思想是将函数作为参数传递,从而实现更加灵活、简洁且具有高度可读性的代码。
在 Java 中,Lambda 表达式的应用不仅仅限于小范围的简化,而是深刻地改变了许多传统的编码模式,尤其是在集合操作、流式处理和并行计算等方面。通过 Stream
API 和函数式接口,Java 允许我们用声明式、函数式的方式来操作数据,极大地提升了代码的可维护性和可扩展性。
在这篇文章中,我们通过一个简单的例子展示了如何利用 Lambda 表达式简化集合中的数据处理。最初,使用传统的方式处理学生数据时,代码显得臃肿且重复。但通过 Lambda 表达式的引入,我们能够清晰地表达意图,并且实现高度简洁的代码。文章将通过多个步骤逐步简化代码,最终展示如何用 Java 8 引入的功能,使得代码既简洁又易于理解。
什么是 Lambda 表达式?
我们知道,对于一个 Java 变量,我们可以给它赋一个“值”,然后可以使用它做一些事情。
Integer a =1;
String s ="Hello";
System.out.println(s + a);
如果我们想将一“段代码”赋值给一个 Java 变量,应该怎么做?
例如,我想把右边的代码块赋值给一个名为 codeBlock 的 Java 变量:
在 Java 8 之前,这是无法实现的。但是在 Java 8 出现之后,可以通过 Lambda 特性来实现。其直观的表达如下:
codeBlock =publicvoiddoSomething(String s){
System.out.println(s);
}
当然,这样的写法不够简洁。所以,为了让这个赋值操作更优雅,我们可以去掉一些多余的声明。
这样,我们就优雅地将“一段代码”赋值给了一个变量。而这个“一段代码”或“赋值给变量的函数”就是 Lambda 表达式。
不过这里仍然有一个问题,那就是 codeBlock 变量应该是什么类型?
在 Java 8 中,所有的 Lambda 类型都是一个接口,而 Lambda 表达式本身,也就是“这段代码”,需要是这个接口的实现。理解 Lambda 的关键之一就在于此。简而言之,Lambda 表达式本身就是一个接口的实现。这么说可能还是有点抽象,所以让我们继续通过示例来说明。我们给上面的 codeBlock 添加一个类型:
这种只有一个方法需要实现的接口称为“函数式接口”。
为了防止后续人员向该接口中添加方法,导致需要实现多个接口方法而成为“非函数式接口”,我们可以在该接口上添加 @FunctionalInterface 声明,以确保他人不能向其添加新方法。
这样,我们就得到了一个完整的 Lambda 表达式声明。
Lambda 表达式的功能是什么?
最直观的效果是使代码变得极其简洁。
我们可以将 Lambda 表达式与传统 Java 对相同接口的实现进行比较:
这两种写法在本质上是等价的。但显然,Java 8 的写法更优雅简洁。此外,由于 Lambda 可以直接赋值给变量,我们可以直接将 Lambda 作为参数传递给函数,而传统的 Java 必须清晰地定义接口实现并进行初始化:
在某些情况下,这种接口实现只需使用一次。传统的 Java 7 要求你定义一个“污染环境”的接口来实现 InterfaceImpl。相比之下,Java 8 的 Lambda 显得更加简洁。
Lambda 结合了 FunctionalInterface Lib、forEach、stream()、方法引用等新特性,使得代码更为简洁!
让我们直接来看示例。
假设已定义了 Student
类和 List<Student>
的值。
@Getter
@AllArgsConstructor
publicstaticclassStudent{
privateString name;
privateInteger age;
}
List<Student> students =Arrays.asList(
newStudent("Bob",18),
newStudent("Ted",17),
newStudent("Zeka",18));
现在你需要打印出 students
中所有 18 岁学生的名字。
@FunctionalInterface
interfaceAgeMatcher{
booleanmatch(Student student);
}
@FunctionalInterface
interfaceExecutor{
booleanexecute(Student student);
}
publicstaticvoidmatchAndExecute(List<Student> students,AgeMatcher matcher,Executor executor){
for(Student student : students){
if(matcher.match(student)){
executor.execute(student);
}
}
}
publicstaticvoidmain(String[] args){
List<Student> students =Arrays.asList(
newStudent("Bob",18),
newStudent("Ted",17),
newStudent("Zeka",18));
matchAndExecute(students,
s -> s.getAge()==18,
s ->System.out.println(s.getName()));
}
这段代码实际上已经相对简洁了,但能否更简洁呢?
当然可以,Java 8 提供了一个函数式接口包,其中定义了很多可能用到的函数式接口(java.util.function
(Java 平台 SE 8))。因此,我们完全不需要定义 AgeMatcher
和 Executor
这两个函数式接口,可以直接使用 Java 8 的函数式接口包中的 Predicate<T>
和 Consumer<T>
,因为它们的接口定义和 AgeMatcher
/Executor
是一样的。
publicstaticvoidmatchAndExecute(List<Student> students,Predicate<Student> predicate,Consumer<Student> consumer){
for(Student student : students){
if(predicate.test(student)){
consumer.accept(student);
}
}
}
matchAndExecute
中的 for each
循环其实很烦人。这里可以使用 Iterable
自带的 forEach()
方法。forEach()
本身可以接受一个 Consumer<T>
参数。
Iterable.forEach()
替换 for each
循环publicstaticvoidmatchAndExecute(List<Student> students,Predicate<Student> predicate,Consumer<Student> consumer){
students.forEach(s ->{
if(predicate.test(s)){
consumer.accept(s);
}
});
}
由于 matchAndExecute
其实只是对 List 的操作,这里可以去掉 matchAndExecute
,直接使用 stream()
特性来完成。stream()
的几个方法可以接受 Predicate<T>
和 Consumer<T>
等参数(java.util.stream
(Java 平台 SE 8))。理解了上述内容后,stream()
就很容易理解,不再需要额外说明。
stream()
代替静态方法students.stream()
.filter(s -> s.getAge()==18)
.forEach(s ->System.out.println(s.getName()));
与原始的 Lambda 写法相比,这样显得更加简洁。但如果我们要求更改为打印学生的全部信息,并将 s -> System.out.println(s)
简化,那么可以使用方法引用继续优化。所谓方法引用,就是用其他对象/类中已有的方法来替代 Lambda 表达式,格式如下:
forEach
中使用方法引用替代 Lambda 表达式students.stream()
.filter(s -> s.getAge()==18)
.map(Student::getName)
.forEach(System.out::println);
这基本上是我能写出的最简洁版本。
关于 Java 中的 Lambda 表达式,还有很多内容值得讨论和学习。例如,如何利用 Lambda 特性来实现并行处理等。
通过本文的示例,我们展示了如何利用 Lambda 表达式简化 Java 代码,使其更加简洁和易于理解。Lambda 的引入不仅减少了冗余代码,还使得操作更加声明式,提升了代码的可读性和可维护性。结合 Stream
API 和方法引用,我们能更加高效地处理数据,同时保持代码的简洁性。
Lambda 表达式是 Java 8 中的重要特性,极大地提高了代码的简洁性与灵活性。在实际开发中,掌握 Lambda 和 Stream API 有助于开发者编写更加简洁、高效的代码,为业务逻辑的实现腾出更多精力。