3.1 Lambda管中窥豹
可以把Lambda表达式理解为一种简洁的可传递匿名函数:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。这个定义够大的,让我们慢慢道来。
- 匿名——说它是匿名的,因为它不像普通的方法那样有一个明确的名称:写得少而想得多!
- 函数——说它是一种函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
- 传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
- 简洁——你无须像匿名类那样写很多模板代码。
你是不是很好奇Lambda这个词是从哪儿来的?其实它起源于学术界开发出的一套用来描述计算的λ演算法。
你为什么应该关心Lambda表达式呢?你在上一章中看到了,在Java中传递代码十分烦琐和冗长。那么,现在有了好消息!Lambda解决了这个问题:它可以让你十分简明地传递代码。理论上来说,你在Java 8之前做不了的事情,Lambda也做不了。但是,现在你用不着再用匿名类写一堆笨重的代码,来体验行为参数化的好处了!Lambda表达式鼓励你采用上一章中提到的行为参数化风格。最终结果就是你的代码变得更清晰、更灵活。比如,利用Lambda表达式,你可以更为简洁地自定义一个Comparator对象。
先前:
Comparator<Apple> byWeight = new Comparator<Apple>() {public int compare(Apple a1, Apple a2){return a1.getWeight().compareTo(a2.getWeight());}};
之后(用了Lambda表达式):
Comparator<Apple> byWeight =(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
不得不承认,代码看起来更清晰了!要是现在你觉得Lambda表达式看起来一头雾水的话也没关系,我们很快会一点点解释清楚的。现在,请注意你基本上只传递了比较两个苹果重量所真正需要的代码。看起来就像是只传递了compare方法的主体。你很快就会学到,你甚至还可以进一步简化代码。下一节会解释在哪里以及如何使用Lambda表达式。
我们刚刚展示给你的Lambda表达式有三个部分,如图3-1所示。

图 3-1 Lambda表达式由参数、箭头和主体组成
- 参数列表——这里它采用了
Comparator中compare方法的参数,两个Apple。 - 箭头——箭头
->把参数列表与Lambda主体分隔开。 - Lambda主体——比较两个
Apple的重量。表达式就是Lambda的返回值。
为了进一步说明,下面给出了Java 8中五个有效的Lambda表达式的例子。
代码清单 3-1 Java 8中有效的Lambda表达式
(String s) -> s.length() ←---- 第一个Lambda表达式具有一个String类型的参数并返回一个int。Lambda没有return语句,因为已经隐含了return(Apple a) -> a.getWeight() > 150 ←---- 第二个Lambda表达式有一个Apple类型的参数并返回一个boolean(苹果的重量是否超过150克)(int x, int y) -> {System.out.println("Result:");System.out.println(x + y);} ←---- 第三个Lambda表达式具有两个int类型的参数而没有返回值(void返回)。注意Lambda表达式可以包含多行语句,这里是两行() -> 42 ←---- 第四个Lambda 表达式没有参数,返回一个int(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) ←---- 第五个Lambda表达式具有两个Apple类型的参数,返回一个int:比较两个Apple的重量
Java语言设计者选择这样的语法,是因为C#和Scala等语言中的类似功能广受欢迎。JavaScript也有类似的语法。Lambda的基本语法是(被称为表达式–风格的Lambda)
(parameters) -> expression
或(请注意语句的花括号,这种Lambda经常被叫作块–风格的Lambda)
(parameters) -> { statements; }
你可以看到,Lambda表达式的语法很简单。做一下测验3.1,看看自己是不是理解了这个模式。
测验3.1:Lambda语法
根据上述语法规则,以下哪个不是有效的Lambda表达式?
(1)
() -> {}(2)
() -> "Raoul"(3)
() -> {return "Mario";}(4)
(Integer i) -> return "Alan" + i;(5)
(String s) -> {"Iron Man";}答案:只有(4)和(5)是无效的Lambda,其余都是有效的。详细解释如下。
(1) 这个Lambda没有参数,并返回
void。它类似于主体为空的方法:public void run() {}。一个有趣的事实:这种Lambda也经常被叫作“汉堡型Lambda”。如果只从一边看,它的形状就像是两块圆面包组成的汉堡。(2) 这个Lambda没有参数,并返回
String作为表达式。(3) 这个Lambda没有参数,并返回
String(利用显式返回语句)。(4)
return是一个控制流语句。要使此Lambda有效,需要使用花括号,如下所示:
(Integer i) -> {return "Alan" + i;}(5)“Iron Man”是一个表达式,不是一个语句。要使此Lambda有效,可以去除花括号和分号,如下所示:
(String s) -> "Iron Man"或者如果你喜欢,可以使用显式返回语句,如下所示:
(String s) -> {return "Iron Man";}
表3-1提供了一些Lambda的例子和使用案例。
表 3-1 Lambda示例
| 使用案例 | Lambda示例 |
|---|---|
| 布尔表达式 |
|
| 创建对象 |
|
| 消费一个对象 |
|
| 从一个对象中选择/抽取 |
|
| 组合两个值 |
|
| 比较两个对象 |
|
