JDK 8 的 Lambda 表达式 -- 写法

过去,我们使用匿名类的实现像  ActionListener 这样的接口,即使最简单的情景都需要写上五六行代码,这就显得有些累赘了。因为其实我们在 new ActionListener {} 时就是在创建一个 ActionListener 子类型,重载了方法,并初始化一个实例出来。现在我们手上有了 Lambda 表达式这一武器的话,就变得简单明了了,所以说  Lambda 是一种轻量级的实现机制。


这里是几个 Lambda 表达式的样子:
1(int x, int y) -> x + y    //两整形参数,返回它们的和
2() -> 42                   //无参数,直接返回 42
3x -> 100;                  //可推断出参数 x 的类型
4(String s) -> { System.out.println(s); } //传入一个字符, 只执行一个操作, 无返回值

Lambda 的格式就是三部分:参数列表,-> 分隔符,执行体。其他语方的 Lambda 格式都这样的,可能就是分隔符不一样,如 Ruby 的 |,Scala 的 => 等。

执行体里的 return 只是标明返回到匿名方法的调用者,不是返回值的意思; 最上层是不能用 break 和 continue 关键字的,可用在循环中; 如果 Lambda 表达式有返回值,那么在每一条路径上都要有返回值或是抛出异常。路径上最后一条语句的返回值即为 Lambda 表达的返回值。

Lambda 尽力的保持最简洁,如果执行体中有多行的代码用大括号括起来,下面是一些 Lambda 表达式的完整例子:
1FileFilter java = (File f) -> f.getName().endsWith(".java");
2
3String user = doPrivileged(() -> System.getProperty("user.name"));
4
5new Thread(() -> {
6    connectToService();
7    sendNotification();
8}).start();

说到这里,应该都知道怎么去写 Lambda 表达式了。Lambda 旨在简化功能性接口(即 SAM 单一抽象方法的接口)的实现,稍具体一点就是干了原来匿名类的事情。再次重复一次,只有功能性接口才能运用于 Lambda 表达式中。官方的文档说到此仍然未交代如果用 Lambda 来实现 ActionListener 应该如何写,只要一行:
1button.addActionListener((ActionEvent e) -> ui.dazzle(e.getModifiers()));

该是深入的时候了。我们来分析一下,button.addActionListener() 方法是要接收一个 ActionListener 实例,也就是说这里的 (ActionEvent e) -> ui.dazzle(e.getModifiers()) 就是一个 ActionListener 实例。 ActionListener 接口定义是
1public interface ActionListener extends EventListener {
2    public void actionPerformed(ActionEvent e);
3}

它就是一个功能性接口(只有一个抽象方法的接口),所以编译器会让我们的 Lambda 表达式 (ActionEvent e) -> ui.dazzle(e.getModifiers()) 明确的映射为对那个唯一方法 public void actionPerformed(ActionEvent e) 的实现,注意它们的参数和返回值必须是完全一致的。如果那个 SAM 是无参数的,就写成 () -> /* do something */。

最能说明问题的也唯有看看分别使用匿名类和 Lambda (匿名方法) 时所产生的字节码,本想在本篇中说明下 Lambda 实现的原理,但会使得此文主题太多,所以另开新篇,这里不妨继续说些 Lambda 其他些事情。

又一个话题,Lambda 对本地变量的访问。我们知道在匿名类中只能访问 final 类型的本地变量,而在 Lambda 表达式中则可以捕获所有的本地变量。可以理解为 Lambda 表达式没有引入新的作用域,但这样理解恐怕有时候也无好处。

同样的,匿名类中的 this 指向匿名类实例,而 Lambda 表达式中的 this 仍然是指向使用它的实例。说实在的,JavaScript 中的 this 更容易把人搞晕了头。

小结:进一步明白 Lambda 只能应用在功能性接口的 SAM(Single Abstract Method) 上,并且要理解 Lambda 表达式的每一部分,包括参数列表,返回值是怎么与 SAM 方法签名相对应的。下一篇打算继续深化 Lambda 表达式的类型推断,也是进一步加强 SAM 与 Lambda 之间的映射。 永久链接 https://yanbin.blog/jdk-8-lambda-2-expression/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。