Kotlin 2.1.0 入门教程(八)

ops/2025/1/24 5:39:26/

Lambda 表达式和匿名函数

Lambda 表达式和匿名函数是函数字面量。

函数字面量是未声明但立即作为表达式传递的函数。考虑以下示例:

kotlin">max(strings, { a, b -> a.length < b.length })

函数 max 是一个高阶函数,因为它将函数值作为其第二个参数。这第二个参数是一个本身为函数的表达式,称为函数字面量,它等效于以下命名函数:

kotlin">fun compare(a: String, b: String): Boolean = a.length < b.length

Lambda 表达式语法

完整语法形式如下:

kotlin">val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
  • Lambda 表达式总是用花括号括起来。

  • 完整语法形式中的参数声明放在花括号内,并且可以选择性地包含类型注解。

  • 主体放在 -> 之后。

  • 如果推断的 Lambda 返回类型不是 Unit,则 Lambda 主体中的最后一个(或可能是唯一的)表达式被视为返回值。

如果省略所有可选的注解,剩下的内容如下所示:

kotlin">val sum = { x: Int, y: Int -> x + y }

传递尾随 Lambda

如果函数的最后一个参数是函数,则可以将作为相应参数传递的 Lambda 表达式放在括号外:

kotlin">val product = items.fold(1) { acc, e -> acc * e }

如果 Lambda 是该调用中的唯一参数,则可以完全省略括号:

kotlin">run { println("...") }

it 单个参数的隐式名称

Lambda 表达式只有一个参数的情况非常常见。

如果编译器可以在没有任何参数的情况下解析签名,则无需声明参数,并且可以省略 ->

该参数将隐式声明为 it

kotlin">ints.filter { it > 0 } // 这个字面量的类型是 (it: Int) -> Boolean

如果 Lambda 表达式只有一个参数,可以使用 it 隐式引用该参数。

kotlin">fun main() {val numbers = listOf(-1, 2, -3, 4)val positives = numbers.filter { it > 0 }
}

Lambda 表达式返回值

可以使用限定的返回语法从 Lambda 中显式返回值。否则,最后一个表达式的值将被隐式返回。

因此,以下两个代码片段是等效的:

kotlin">ints.filter {val shouldFilter = it > 0shouldFilter
}ints.filter {val shouldFilter = it > 0return@filter shouldFilter
}

这种约定,连同将 Lambda 表达式放在括号外传递,允许编写 LINQ 风格的代码:

kotlin">strings.filter { it.length == 5 }.sortedBy { it }.map { it.uppercase() }

未使用变量的下划线

如果 Lambda 参数未使用,可以用下划线代替其名称:

kotlin">map.forEach { (_, value) -> println("$value!") }

匿名函数

上面的 Lambda 表达式语法缺少一件事:指定函数返回类型的能力。

在大多数情况下,这是不必要的,因为返回类型可以自动推断。

但是,如果确实需要显式指定它,可以使用另一种语法:匿名函数。

kotlin">fun (x: Int, y: Int): Int = x + y

匿名函数看起来非常像常规函数声明,只是省略了名称。其主体可以是表达式(如上所示)或代码块:

kotlin">fun (x: Int, y: Int): Int {return x + y
}

参数和返回类型的指定方式与常规函数相同,但如果可以从上下文中推断出参数类型,则可以省略参数类型:

kotlin">ints.filter(fun (item) = item > 0)

匿名函数的返回类型推断与普通函数一样:对于具有表达式主体的匿名函数,返回类型会自动推断,但对于具有代码块主体的匿名函数,必须显式指定返回类型(或假定为 Unit)。

当将匿名函数作为参数传递时,请将其放在括号内。允许将函数放在括号外的简写语法仅适用于 Lambda 表达式。

Lambda 表达式和匿名函数之间的另一个区别是非局部返回的行为。没有标签的 return 语句总是从使用 fun 关键字声明的函数返回。这意味着 Lambda 表达式中的 return 将从封闭函数返回,而匿名函数中的 return 将从匿名函数本身返回。

闭包

Lambda 表达式或匿名函数(以及局部函数和对象表达式)可以访问其闭包,闭包包括在外部作用域中声明的变量。在闭包中捕获的变量可以在 Lambda 中修改:

kotlin">var sum = 0
ints.filter { it > 0 }.forEach {sum += it
}
print(sum)

带有接收者的函数字面量

带有接收者的函数类型,例如 A.(B) -> C,可以用一种特殊形式的函数字面量实例化:带有接收者的函数字面量。

如上所述,Kotlin 提供了在提供接收者对象的同时调用带有接收者的函数类型实例的能力。

在函数字面量的主体内,传递给调用的接收者对象成为隐式的 this,因此您可以访问该接收者对象的成员而无需任何额外的限定符,或者使用 this 表达式访问接收者对象。

这种行为类似于扩展函数,扩展函数也允许您在函数体内访问接收者对象的成员。

以下是带有接收者的函数字面量及其类型的示例,其中在接收者对象上调用了 plus

kotlin">val sum: Int.(Int) -> Int = { other -> plus(other) }

匿名函数语法允许您直接指定函数字面量的接收者类型。如果您需要声明一个带有接收者的函数类型变量,然后稍后使用它,这可能很有用。

kotlin">val sum = fun Int.(other: Int): Int = this + other

当可以从上下文中推断出接收者类型时,Lambda 表达式可以用作带有接收者的函数字面量。它们最重要的用途之一是类型安全构建器:

kotlin">class HTML {fun body() { ... }
}fun html(init: HTML.() -> Unit): HTML {val html = HTML()  // 创建接收者对象。html.init()        // 将接收者对象传递给 lambda。return html
}html {       // 带有接收者的 lambda 从这里开始。body()   // 调用接收者对象的方法。
}
kotlin">class HTML {fun body() {println("body")}
}fun html(init: HTML.() -> Unit): HTML {val html = HTML()html.init()return html
}fun main() {html {body() // bodythis.body() // body}
}
kotlin">fun html(init: HTML.() -> Unit): HTML {val html = HTML()html.init()return html
}class HTML {fun head(init: Head.() -> Unit) {val head = Head()head.init()}fun body(init: Body.() -> Unit) {val body = Body()body.init()}
}class Head {fun title(text: String) {println("<title>$text</title>")}
}class Body {fun p(text: String) {println("<p>$text</p>")}
}fun main() {html {head {title("My Page")}body {p("Hello, Kotlin!")}}
}

http://www.ppmy.cn/ops/152663.html

相关文章

docker 安装 redis 详解

在平常的开发工作中&#xff0c;我们经常会用到 redis&#xff0c;那么 docker 下应该如何安装 redis 呢&#xff1f;简单来说&#xff1a;第一步&#xff1a;拉取redis镜像&#xff1b;第二步&#xff1a;设置 redis.conf 配置文件&#xff1b;第三步&#xff1a;编写 docker-…

C++函数——fill

在C中&#xff0c;std::fill 是标准库提供的一个算法适用于几乎所有类型的容器&#xff0c;只要这些容器支持迭代器操作。具体来说&#xff0c;std::fill 的适用性取决于容器是否提供了满足其要求的迭代器类型&#xff0c;用于将指定范围内的所有元素设置为某个特定值。它是一个…

【Java-数据结构】Java 链表面试题上 “最后一公里”:解决复杂链表问题的致胜法宝

我的个人主页 我的专栏&#xff1a;Java-数据结构&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 引言&#xff1a; Java链表&#xff0c;看似简单的链式结构&#xff0c;却蕴含着诸多有趣的特性与奥秘&#xff0c;等待我们去挖掘。它就像一…

(4)ACS控制器Buffer程序记录与解析-PEG功能程序

一、pandas是什么&#xff1f; 示例&#xff1a;pandas 是基于NumPy 的一种工具&#xff0c;该工具是为了解决数据分析任务而创建的。 二、使用步骤 1.程序 代码如下&#xff08;示例&#xff09;&#xff1a; ASSIGNPEG(0),4,0 ASSIGNPOUTS(0),0,0 ENABLE 0 ACC(0)10000 …

【浙江省乡镇界】面图层shp格式arcgis数据+乡镇名称和编码+wgs84坐标无偏移内容测评

最新2020年乡镇界面图层shp格式arcgis数据乡镇名称和编码wgs84坐标无偏移。arcgis直接打开&#xff0c;单独乡镇界一个图层。品质高

密码无关认证:金融机构如何解决密码问题

密码安全问题&#xff0c;依然是金融行业面临的重大挑战。尽管密码简单易用&#xff0c;但许多金融机构仍然依赖这种方式进行身份认证。幸运的是&#xff0c;随着技术的发展&#xff0c;密码无关认证已经成为一种更加安全、便捷的选择&#xff0c;它能够为数字银行带来更好的用…

root用户Linux银河麒麟服务器安装vnc服务

安装必要桌面环境组件 yum install mate-session-manager -y mate-session #确定是否安装成功安装vnc服务器 yum install tigervnc-server -y切换到root为root得vnc设置密码 su root vncpasswd给root用户设置vnc服务器文件 vi /etc/systemd/system/vncserver:1.service [Un…

【2024年 CSDN博客之星】我的2024年创作之旅:从C语言到人工智能,个人成长与突破的全景回顾

我的2024年创作之旅&#xff1a;从C语言到人工智能&#xff0c;个人成长与突破的全景回顾 引言 回望2024年&#xff0c;我不仅收获了技术上的成长&#xff0c;更收获了来自CSDN平台上无数粉丝、朋友以及网友们的支持与鼓励。在这条创作之路上&#xff0c;CSDN不仅是我展示技术成…