第07部分:lambda表达式

Java 8 引入的功能中,最让人期盼的是 lambda 表达式。lambda 表达式以字面量的形式把少量代码直接写在程序中,而且让 Java 编程更符合函数式风格。

其实,lambda 表达式的很多功能都能使用嵌套类型通过回调和处理程序等模式实现,但使用的句法总是非常冗长,尤其是,就算只需要在回调中编写一行代码,也要完整定义一个新类型。

前面见过,lambda 表达式的句法是一个参数列表和方法主体,如下所示:(p, q) -> { /* 方法主体 */ },这种句法能通过一种十分紧凑的方式表示简单的方法,而且能很大程度上避免使用匿名类。组成方法的各个部分,lambda 表达式几乎都有,不过显然,lambda 表达式没有名称。其实,有些开发者喜欢把 lambda 表达式当成“匿名方法”。例如,java.io.File 类的 list() 方法。这个方法列出一个目录中的文件,但在返回列表之前,要把每个文件的名称传给 FilenameFilter 对象,而这个对象必须由你提供。FilenameFilter 对象用于接受或拒绝各个文件。

使用匿名类可以按照如下的方式定义一个 FilenameFilter 类,只列出文件名以 .java 结尾的文件:

使用 lambda 表达式,上述代码可以简化成:

对目录中的每个文件来说,都会执行 lambda 表达式中的代码。如果这个方法的返回值是true,对应的文件就会出现在输出中,即存入数组 filelist 中。这种模式叫过滤器,即使用一个代码块测试容器中的元素是否匹配某个条件,并且只返回能通过条件的元素。过滤器是函数式编程的标准技术之一,稍后会详细说明。





转换lambda表达式

javac 遇到 lambda 表达式时会把它解释为一个方法的主体,这个方法具有特定的签名。不过,是哪个方法呢?

为了解决这个问题,javac 会查看周围的代码。lambda 表达式必须满足以下条件才算是合法的 Java 代码:

• lambda 表达式必须出现在期望使用接口类型实例的地方;

• 期望使用的接口类型必须只有一个强制方法;

• 这个强制方法的签名要完全匹配 lambda 表达式。

如果满足上述条件,编译器会创建一个类型,实现期望使用的接口,然后把 lambda 表达式的主体当作强制方法的实现。

说得稍微复杂一点儿,这么做是为了保持 Java 类型系统的名义(基于名称)纯粹性。也就是说,lambda 表达式会被转换成正确接口类型的实例。

有些开发者还喜欢使用“单一抽象方法”(Single Abstract Method,SAM)类型这个术语表示 lambda 表达式转换得到的接口类型。这表明,若想在 lambda 表达式机制中使用某个接口,这个接口必须只有一个非默认方法。

虽然 lambda 表达式和匿名类有很多相似之处,但 lambda 表达式并不只是匿名类的语法糖。其实,lambda 表达式使用方法句柄和一个特殊的新 JVM 字节码 invokedynamic 实现。

从上述讨论可以看出,Java 8 添加的 lambda 表达式经过精心设计,以适应 Java 现有的类型系统——这个系统十分注重名义类型。





方法引用

前面说过,可以把 lambda 表达式看成没有名称的方法。对下面的 lambda 表达式来说:

会自动转换成对 @FunctionalInterface 接口的实现,这个接口只有一个非默认方法,这个方法接受一个 MyObject 类型的参数,返回值类型为 String。不过,这里的样板代码太多,所以 Java 8 提供了一种句法,可以让这种 lambda 表达式更易于阅读和编写:

这种简写形式叫方法引用(method reference),使用现有的方法作为 lambda 表达式。方法引用就像是使用现有的方法,但会忽略方法的名称,所以能作为 lambda 表达式使用,而且能使用往常的方式自动转换。





函数式编程

Java 实质上是面向对象语言。不过,引入 lambda 表达式后,可以更轻易地编写符合函数式风格的代码。

关于函数式语言由什么组成,没有明确的定义,但至少有一个共识:函数式语言最起码要能把函数当成值,存入变量。

Java(从 1.1 版起)一直都能通过内部类表示函数,但句法很复杂,代码结构不清晰。lambda 表达式大大简化了这种句法,因此,越来越多的开发者会在 Java 代码中寻求使用函数式编程风格,这是很自然的,而且现在做起来也更容易。

Java(从 1.1 版起)一直都能通过内部类表示函数,但句法很复杂,代码结构不清晰。lambda 表达式大大简化了这种句法,因此,越来越多的开发者会在 Java 代码中寻求使用函数式编程风格,这是很自然的,而且现在做起来也更容易。

Java 开发者初尝函数式编程时有可能会使用如下三个非常有用的基本习语。

• map()

map()用于列表和类似列表的容器。运作原理是,传入一个函数,应用于集合中的各个元素,得到一个新集合。新集合中保存的是在各个元素上执行函数后得到的结果。这意味着,map() 可能会把一种类型的集合转换成另一种类型的集合。

• filter()

说明如何把匿名类实现的 FilenameFilter 换成 lambda 表达式实现时,见过使用filter() 的示例。filter() 基于某种条件生成一个集合的子集。注意,在函数式编程中,一般会生成新集合,而不直接修改现有的集合。

• reduce()

reduce()有几种不同的形式,执行的是聚合运算,除了叫化简之外,还可以叫合拢、累计或聚合。基本原理是,提供一个初始值和聚合函数(或化简函数),然后在各个元素上执行这个化简函数,在化简函数遍历整个集合的过程中会得到一系列中间值(类似于“累积计数”),最后得到一个最终结果。

Java 完全支持这些重要的函数式习语(除此之外还有几个)。后面会稍微深入地说明这些习语的实现方式,届时会介绍 Java 的数据结构和集合,以及抽象流。抽象流是实现这些习语的基础。

对 lambda 表达式的介绍到此结束,下面是一些注意事项。值得注意的是,最好把 Java 看成轻度支持函数式编程的语言。Java 不是专门的函数式语言,也不想变成函数式语言。Java 的某些特性决定了它不可能是函数式语言,具体而言有以下几点。

Java 没有结构类型,因此没有“真正的”函数类型。每个 lambda 表达式都会自动转换成适当的名义类型。

类型擦除在函数式编程中会导致问题——高阶函数的类型安全性会丢失。

Java 天生可改变——一般认为,可变性是函数式语言极不需要的特性。

抛开这些,能轻易使用基本的函数式编程风格,尤其是 map()、filter() 和 reduce() 等习语,是 Java 社区向前迈出的一大步。这些习语非常有用,因此绝大多数 Java 开发者都不需要也不会错过纯正函数式语言提供的高级功能。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容