作用域函数在 Kotlin 中非常有用,可以帮助我们管理代码并编写清晰易读的代码。
什么是作用域函数?
Kotlin 标准库中包含几个函数,其唯一目的是在对象的上下文中执行一段代码块。当我们在对象上调用这样的函数并提供一个 lambda 表达式时,它形成了一个临时作用域。在这个作用域中,我们可以通过对象的属性和函数来访问该对象,而无需使用对象的名称。这些函数被称为作用域函数。Kotlin 中共有五个作用域函数:let
、run
、with
、apply
和 also
。
关于 this
和 it
this
:在run
、with
和apply
函数中,我们可以使用 lambda 接收者关键字this
来引用上下文对象。因此,在它们的 lambda 表达式中,可以像在普通类函数中一样访问对象。在大多数情况下,当访问接收者对象的成员时,我们可以省略this
,从而使代码更简洁。然而,如果省略了this
,很难区分接收者成员和外部对象或函数之间的区别。因此,在主要通过调用其函数或为属性赋值来操作对象成员的 lambda 中,建议将上下文对象作为接收者 (this
)。
val adam = Person("Adam").apply {
age = 20 // 与 this.age = 20 相同
city = "London"
}
println(adam)
it
:let
和also
函数将上下文对象作为 lambda 参数引用。如果未指定参数名称,则可以使用隐式的默认名称it
来访问对象。使用it
比使用this
更简洁,使用it
的表达式通常更易读。然而,当调用对象的函数或属性时,不能像使用this
那样隐式地访问对象。因此,当对象主要作为函数调用的参数时,通过it
访问上下文对象更好。如果在代码块中使用多个变量,则使用it
也更好。
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() 生成的值为 $it")
}
}
val i = getRandomInt()
println(i)
使用作用域函数的应用场景
作用域函数可以使代码更加清晰、易读和简洁,这是 Kotlin 语言的主要特点之一。
作用域函数的类型有五种:let、run、with、apply、also
这些函数之间的主要区别有两点:
- 引用上下文对象的方式(使用
this
或it
关键字) - 返回值(返回上下文对象或 lambda 结果)
作用域函数比较表:
函数 | 上下文对象引用 | 返回值 |
let |
it |
lambda 结果 |
run |
this |
lambda 结果 |
with |
this |
lambda 结果 |
apply |
this |
对象本身 |
also |
it |
对象本身 |
let
函数
- 上下文对象:作为参数(
it
) - 返回值:lambda 结果
使用场景:let
函数经常用于处理可空对象以避免空指针异常。可以使用安全调用操作符(?.
)结合 let
来进行空安全调用。它仅在非空值时执行代码块。
可以用于在调用链中的结果上调用一个或多个函数。
示例:
// 链式调用
val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)
// 使用 let
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }
.filter { it > 3 }
.let { println(it) } // 可以继续添加更多函数调用
空变量检查
val str: String? = "Hello"
// processNonNullString(str) // 编译错误:str 可能为空
val length = str?.let {
println("let() 在 $it 上调用")
processNonNullString(it) // OK:'it' 在 '?.let { }' 内部不为空
it.length
}
with
函数
- 上下文对象:作为接收者(
this
) - 返回值:lambda 结果
使用场景:推荐使用 with
在上下文对象上调用函数,而不提供 lambda 结果。在代码中,我们可以将 with
理解为“对于这个对象,执行以下操作”。
示例:
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' 被调用,参数为 $this")
println("它包含 $size 个元素")
}
run
函数
- 上下文对象:作为接收者(
this
) - 返回值:lambda 结果
使用场景:run
在 lambda 中既可以初始化对象,又可以计算返回值。使用 run
我们可以进行空安全调用以及其他计算操作。
示例:
初始化和计算
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
链式空检查
val firstName: String? = null
var middleName: String? = null
var lastName: String? = null
middleName = "M "
lastName = "Vasava"
firstName?.run {
val fullName = this + middleName + lastName
print(fullName) // 仅打印 M Vasava
}
apply
函数
- 上下文对象:作为接收者(
this
) - 返回值:对象本身
使用场景:我们建议在不返回值的代码块中使用 apply
,主要用于操作接收者对象的成员。最常见的用例是对象配置。我们可以理解这样的调用为“将以下赋值应用于该对象”。
示例:
val adam = Person("Adam").apply {
name = "Adam"
age = 20
city = "London"
}
println(adam)
also函数
- 上下文对象:作为参数(it)。
- 返回值:对象本身。
使用场景:可用于需要引用对象而不是其属性和函数的操作,或者当您不想从外部作用域隐藏 this 引用时。 当在代码中看到also时,可以将其读作“并且还对对象执行以下操作”。
来源:kotlin社区的文章