3.1 Lambda管中窥豹

可以把Lambda表达式理解为一种简洁的可传递匿名函数:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。这个定义够大的,让我们慢慢道来。

  • 匿名——说它是匿名的,因为它不像普通的方法那样有一个明确的名称:写得少而想得多!
  • 函数——说它是一种函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
  • 传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
  • 简洁——你无须像匿名类那样写很多模板代码。

你是不是很好奇Lambda这个词是从哪儿来的?其实它起源于学术界开发出的一套用来描述计算的λ演算法

你为什么应该关心Lambda表达式呢?你在上一章中看到了,在Java中传递代码十分烦琐和冗长。那么,现在有了好消息!Lambda解决了这个问题:它可以让你十分简明地传递代码。理论上来说,你在Java 8之前做不了的事情,Lambda也做不了。但是,现在你用不着再用匿名类写一堆笨重的代码,来体验行为参数化的好处了!Lambda表达式鼓励你采用上一章中提到的行为参数化风格。最终结果就是你的代码变得更清晰、更灵活。比如,利用Lambda表达式,你可以更为简洁地自定义一个Comparator对象。

先前:

  1. Comparator<Apple> byWeight = new Comparator<Apple>() {
  2. public int compare(Apple a1, Apple a2){
  3. return a1.getWeight().compareTo(a2.getWeight());
  4. }
  5. };

之后(用了Lambda表达式):

  1. Comparator<Apple> byWeight =
  2. (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

不得不承认,代码看起来更清晰了!要是现在你觉得Lambda表达式看起来一头雾水的话也没关系,我们很快会一点点解释清楚的。现在,请注意你基本上只传递了比较两个苹果重量所真正需要的代码。看起来就像是只传递了compare方法的主体。你很快就会学到,你甚至还可以进一步简化代码。下一节会解释在哪里以及如何使用Lambda表达式。

我们刚刚展示给你的Lambda表达式有三个部分,如图3-1所示。

3.1 Lambda管中窥豹 - 图1

图 3-1 Lambda表达式由参数、箭头和主体组成

  • 参数列表——这里它采用了Comparatorcompare方法的参数,两个Apple
  • 箭头——箭头->把参数列表与Lambda主体分隔开。
  • Lambda主体——比较两个Apple的重量。表达式就是Lambda的返回值。

为了进一步说明,下面给出了Java 8中五个有效的Lambda表达式的例子。

代码清单 3-1 Java 8中有效的Lambda表达式

  1. (String s) -> s.length() ←---- 第一个Lambda表达式具有一个String类型的参数并返回一个intLambda没有return语句,因为已经隐含了return
  2. (Apple a) -> a.getWeight() > 150 ←---- 第二个Lambda表达式有一个Apple类型的参数并返回一个boolean(苹果的重量是否超过150克)
  3. (int x, int y) -> {
  4. System.out.println("Result:");
  5. System.out.println(x + y);
  6. } ←---- 第三个Lambda表达式具有两个int类型的参数而没有返回值(void返回)。注意Lambda表达式可以包含多行语句,这里是两行
  7. () -> 42 ←---- 第四个Lambda 表达式没有参数,返回一个int
  8. (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) ←---- 第五个Lambda表达式具有两个Apple类型的参数,返回一个int:比较两个Apple的重量

Java语言设计者选择这样的语法,是因为C#和Scala等语言中的类似功能广受欢迎。JavaScript也有类似的语法。Lambda的基本语法是(被称为表达式–风格的Lambda)

  1. (parameters) -> expression

或(请注意语句的花括号,这种Lambda经常被叫作块–风格的Lambda)

  1. (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有效,需要使用花括号,如下所示:

  1. (Integer i) -> {return "Alan" + i;}

(5)“Iron Man”是一个表达式,不是一个语句。要使此Lambda有效,可以去除花括号和分号,如下所示:

  1. (String s) -> "Iron Man"

或者如果你喜欢,可以使用显式返回语句,如下所示:

  1. (String s) -> {return "Iron Man";}

表3-1提供了一些Lambda的例子和使用案例。

表 3-1 Lambda示例

使用案例 Lambda示例
布尔表达式
  1. (List<pString> list) -> list.isEmpty()
创建对象
  1. () -> new Apple(10)
消费一个对象
  1. (Apple a) -> {
    System.out.println(a.getWeight());
    }
从一个对象中选择/抽取
  1. (String s) -> s.length()
组合两个值
  1. (int a, int b) -> a * b
比较两个对象
  1. (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())