作用域函数在 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
关键字)作用域函数比较表:
函数 | 上下文对象引用 | 返回值 |
let |
it |
lambda 结果 |
run |
this |
lambda 结果 |
with |
this |
lambda 结果 |
apply |
this |
对象本身 |
also |
it |
对象本身 |
let
函数it
)使用场景: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
) 使用场景:推荐使用 with
在上下文对象上调用函数,而不提供 lambda 结果。在代码中,我们可以将 with
理解为“对于这个对象,执行以下操作”。
示例:
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' 被调用,参数为 $this")
println("它包含 $size 个元素")
}
run
函数this
)使用场景: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)
使用场景:可用于需要引用对象而不是其属性和函数的操作,或者当您不想从外部作用域隐藏 this 引用时。 当在代码中看到also时,可以将其读作“并且还对对象执行以下操作”。
来源:kotlin社区的文章