中级教程: 带接受者的 Lambda 表达式
在这一章中, 你将学习在另一种函数类型 Lambda 表达式中如何使用接受者对象, 以及它们如何帮助你创建一个特定领域专用语言(Domain-Specific Language, DSL).
带接受者的 Lambda 表达式
在初学者教程中, 你已经学习了如何使用 Lambda 表达式. Lambda 表达式也可以带有接受者. 这种情况下, Lambda 表达式能够访问接受者对象的任何成员函数或属性, 而不必每次都明确的指明接受者对象. 没有了这些额外的引用, 你的代码会变得更加易于阅读和维护.
带接受者的 Lambda 表达式的语法与定义函数类型时不同. 首先, 请写下你想要扩展的接受者类型. 之后, 是一个 .
号, 之后写下你的函数类型定义的其它部分. 例如:
这个函数类型:
接受者类型是
MutableList<Int>
.括号
()
之内没有函数参数.没有返回值:
Unit
.
我们来看看下面的实例, 它扩展 StringBuilder
类:
在这个示例中:
接受者类型是
StringBuilder
类.Lambda 表达式的函数类型没有函数参数
()
, 也没有返回值Unit
.Lambda 表达式调用
StringBuilder
类的append()
成员函数, 使用字符串"Hello!"
作为函数参数.创建了
StringBuilder
类的一个实例.Lambda 表达式赋值给变量
appendText
, 并在stringBuilder
实例上调用.使用
toString()
函数,stringBuilder
实例被转换为字符串, 并通过println()
函数打印输出.
如果你想要创建一个特定领域专用语言(Domain-Specific Language, DSL), 带接受者的 Lambda 表达式会非常有用. 因为你可以访问接受者对象的成员函数和属性, 而不必明确引用接受者, 你的代码会变得更加精简.
为了演示这一点, 我们来考虑一个配置菜单中项目的示例. 我们从一个 MenuItem
类和一个 Menu
类开始, Menu
类包含一个向菜单中添加项目的函数, 名为 item()
, 以及一个包含所有项目的列表, 名为 items
:
我们使用一个带接受者的 Lambda 表达式, 将它作为函数参数 (init
) 传递给 menu()
函数, 这个函数构建一个菜单, 作为开始点. 你会注意到, 这段代码使用了与前面的 StringBuilder
类的示例类似的方案:
现在你可以使用这个 DSL 来配置一个菜单, 并创建一个 printMenu()
函数, 将菜单结构打印输出到控制台:
如你所见, 使用带接受者的 Lambda 表达式 大大的简化了创建你的菜单所需要的代码. Lambda 表达式不仅仅可以用于设置和创建, 也可以用于配置. 它们普遍用于构建 API, UI 框架, 以及配置构建器的 DSL, 以生成精简的代码, 让你能够更容易的专注于底层代码结构和逻辑.
Kotlin 的生态环境中存在这种设计模式的很多例子, 例如标准库中的 buildList()
和 buildString()
函数.
实际练习
习题 1
你有一个 fetchData()
函数, 参数是一个带接受者的 Lambda 表达式. 请更新 Lambda 表达式, 使用 append()
函数, 让你的代码的输出成为: Data received - Processed
.
习题 2
你有一个 Button
类, 以及 ButtonEvent
和 Position
数据. 请编写代码, 触发 Button
类的 onEvent()
成员函数, 触发一个 double-click 事件. 你的代码应该打印输出 "Double click!"
.
习题 3
编写一个函数, 创建一个整数 List 的副本, 其中每个元素增大 1. 请使用已经提供的函数框架, 它使用一个 incremented
函数扩展了 List<Int>
.