通俗易懂!Java Lambda表达式最全讲解,轻松掌握核心概念

编程疏影 路条编程 2024年12月13日 07:30
图片

通俗易懂!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(+ 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 岁学生的名字。

原始的 Lambda 写法:定义两个函数式接口、定义一个静态方法、调用静态方法并将 Lambda 表达式赋值给参数。

@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(->{
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.getAge()==18)
.forEach(->System.out.println(s.getName()));

与原始的 Lambda 写法相比,这样显得更加简洁。但如果我们要求更改为打印学生的全部信息,并将 s -> System.out.println(s) 简化,那么可以使用方法引用继续优化。所谓方法引用,就是用其他对象/类中已有的方法来替代 Lambda 表达式,格式如下:

第四步:简化 — 在 forEach 中使用方法引用替代 Lambda 表达式

students.stream()
.filter(-> s.getAge()==18)
.map(Student::getName)
.forEach(System.out::println);

这基本上是我能写出的最简洁版本。

关于 Java 中的 Lambda 表达式,还有很多内容值得讨论和学习。例如,如何利用 Lambda 特性来实现并行处理等。

结论

通过本文的示例,我们展示了如何利用 Lambda 表达式简化 Java 代码,使其更加简洁和易于理解。Lambda 的引入不仅减少了冗余代码,还使得操作更加声明式,提升了代码的可读性和可维护性。结合 Stream API 和方法引用,我们能更加高效地处理数据,同时保持代码的简洁性。

Lambda 表达式是 Java 8 中的重要特性,极大地提高了代码的简洁性与灵活性。在实际开发中,掌握 Lambda 和 Stream API 有助于开发者编写更加简洁、高效的代码,为业务逻辑的实现腾出更多精力。


今天就讲到这里,如果有问题需要咨询,大家可以直接留言或扫下方二维码来知识星球找我,我们会尽力为你解答。