Java:最容易理解的 Lambda 表达式

在这里插入图片描述

什么是 Lambda?

我们知道,对于 Java 变量,我们可以为其分配一个 “值”,可以用变量实现一些逻辑。

Integer a = 1;
String s = "Hello";
System.out.println(s + a);

如果要将“一段代码”分配给 Java 变量,您应该怎么做?

例如,我想将右侧的代码块分配给名为 codeBlock 的 Java 变量:

在这里插入图片描述
在 Java 8 之前,这是不可能的。但是在 Java 8 出现后,可以使用 Lambda 表达式来完成。最直观的表达式如下:

codeBlock = public void doSomething(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
public static class Student {
  private String name;
  private Integer age;
}

List<Student> students = Arrays.asList(
  new Student("Bob", 18),
  new Student("Ted", 17),
  new Student("Zeka", 18));

现在您需要在 Students 中打印出所有 18 岁学生的姓名。

原始 Lambda 编写方式:定义两个函数接口,定义一个静态函数,调用静态函数,并为参数分配 Lambda 表达式。

@FunctionalInterface
interface AgeMatcher {
  boolean match(Student student);
}
@FunctionalInterface
interface Executor {
  boolean execute(Student student);
}
public static void matchAndExecute(List<Student> students, AgeMatcher matcher, Executor executor) {
  for (Student student : students) {
    if (matcher.match(student)) {
      executor.execute(student);
    }
  }
}

public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student("Bob", 18),
                new Student("Ted", 17),
                new Student("Zeka", 18));
        matchAndExecute(students, 
                        s -> s.getAge() == 18, 
                        s -> System.out.println(s.getName()));
}

这段代码其实比较简洁,但是我们能不能再简洁一点呢?

当然,Java 8 中有一个功能接口包,它定义了大量可以使用的功能接口 (java.util.function (Java Platform SE 8))。

因此,我们根本不需要在这里定义 AgeMatcher 和 Executor 这两个功能接口。我们可以在 Java 8 功能接口包中只使用 Predicate(T) 和 Consumer(T),因为它们有一对接口。该定义实际上与 AgeMatcher/Executor 相同。

在这里插入图片描述
**步骤1:**简化 — 利用功能性接口包:

public static void matchAndExecute(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) 参数。

**步骤2:**Simplify — 将 foreach 循环替换为 Iterable.forEach() :

public static void matchAndExecute(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 Platform SE 8))。一旦你理解了上面的内容,stream() 就很容易理解了,不需要任何进一步的解释。

**步骤3:**Simplify — 使用 stream() 而不是静态函数:

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

相比于原来的 Lambda 写入方法,这非常简洁。但是,如果我们要求更改以打印学生的所有信息,以及 s -> System.out.println(s);那么我们可以使用 Method reference 来继续简化。所谓 Method 引用,就是将 Lambda 表达式替换为其他已编写的 Object/Class 的方法。格式如下:
在这里插入图片描述
**步骤4:**简化 – 您可以在 forEach 中使用方法引用而不是 Lambda 表达式:

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

关于 Java 中的 Lambda,如果大家感兴趣,我会在后续持续更新相关内容

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值