这篇文章将为大家详细讲解有关如何辨别Kotlin的作用域函数,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
作用域函数
Kotlin 的作用域函数有五种:let、run、with、apply 以及 also。
这些函数基本上做了同样的事情:在一个对象上执行一个代码块。
下面是作用域函数的典型用法:
val adam = Person("Adam").apply {
age = 20
city = "London"
}
println(adam)
如果不使用 apply 来实现,每次给新创建的对象属性赋值时就必须重复其名称。
val adam = Person("Adam")
adam.age = 20
adam.city = "London"
println(adam)
作用域函数没有引入任何新的技术,但是它们可以使你的代码更加简洁易读。
事实上,同样的功能可能多个作用域函数都能实现,但我们应该根据不同的场景和需求来使用合适的作用域函数,以求更优雅的实现。
如果想直接查看作用域函数之间的区别与使用场景归纳表,请点击这里。
下面我们将详细描述这些作用域函数的区别及约定用法。
区别
作用域函数之间有两个主要区别:
引用上下文对象的方式:this 还是 it
在作用域函数的 lambda 表达式里,上下文对象可以不使用其实际名称而是使用一个更简短的引用(this 或 it)来访问。
作用域函数引用上下文对象有两种方式:
- 作为 lambda 表达式的接收者(this): run、with、apply
- 作为 lambda 表达式的参数(it): let、also
fun main() {
val str = "Hello"
// this
str.run {
println("The receiver string length: $length")
//println("The receiver string length: ${this.length}") // 和上句效果相同
}
// it
str.let {
println("The receiver string's length is ${it.length}")
}
}
作为 lambda 表达式的接收者
run、with 以及 apply 将上下文对象作为 lambda 表达式接收者,通过关键字 this 引用上下文对象。可以省略 this,使代码更简短。
使用场景:主要对上下文对象的成员进行操作(访问属性或调用函数)。
val adam = Person("Adam").apply {
age = 20 // 和 this.age = 20 或者 adam.age = 20 一样
city = "London"
}
println(adam)
作为 lambda 表达式的参数
let 及 also 将上下文对象作为 lambda 表达式参数。如果没有指定参数名,对象可以用隐式默认名称 it 访问。it 比 this 简短,带有 it 的表达式通常更容易阅读。然而,当调用对象函数或属性时,不能像 this 这样隐式地访问对象。
使用场景:主要对上下文对象进行操作,作为参数使用。
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
此外,当将上下文对象作为参数传递时,可以为上下文对象指定在作用域内的自定义名称(为了提高代码的可读性)。
fun getRandomInt(): Int {
return Random.nextInt(100).also { value ->
writeToLog("getRandomInt() generated value $value")
}
}
val i = getRandomInt()
返回值
根据返回结果,作用域函数可以分为以下两类:
- 返回上下文对象:apply、also
- 返回 lambda 表达式结果:let、run、with
可以根据在代码中的后续操作来选择适当的函数。
返回上下文对象
apply 及 also 的返回值是上下文对象本身。因此,它们可以作为辅助步骤包含在调用链中:你可以继续在同一个对象上进行链式函数调用。
val numberList = mutableListOf<Double>()
numberList.also { println("Populating the list") }
.apply {
add(2.71)
add(3.14)
add(1.0)
}
.also { println("Sorting the list") }
.sort()
它们还可以用在返回上下文对象的函数的 return 语句中。
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
返回lambda表达式结果
let、run 及 with 返回 lambda 表达式的结果。所以,在需要使用其结果给一个变量赋值,或者在需要对其结果进行链式操作等情况下,可以使用它们。
val numbers = mutableListOf("one", "two", "three")
val countEndsWithE = numbers.run {
add("four")
add("five")
count { it.endsWith("e") }
}
println("There are $countEndsWithE elements that end with e.")
此外,还可以忽略返回值,仅使用作用域函数为变量创建一个临时作用域。
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
val firstItem = first()
val lastItem = last()
println("First item: $firstItem, last item: $lastItem")
}
约定用法
let
上下文对象 作为 lambda 表达式的 参数(it)来访问。 返回值 是 lambda 表达式的结果。
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)
// 如果需要可以调用更多函数
}
若代码块仅包含以 it 作为参数的单个函数,则可以使用方法引用(::)代替 lambda 表达式:
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let(::println)
let 经常用于 仅使用非空值执行代码块。如需对非空对象执行操作,可对其使用安全调用操作符 ?. 并调用 let 在 lambda 表达式中执行操作。
val str: String? = "Hello"
//processNonNullString(str) // 编译错误:str 可能为空
val length = str?.let {
println("let() called on $it")
processNonNullString(it) // 编译通过:'it' 在 '?.let { }' 中必不为空
it.length
}
使用 let 的另一种情况是引入作用域受限的局部变量以提高代码的可读性。如需为上下文对象定义一个新变量,可提供其名称作为 lambda 表达式参数来替默认的 it。
val numbers = listOf("one", "two", "three", "four")
val modifiedFirstItem = numbers.first().let { firstItem ->
println("The first item of the list is '$firstItem'")
if (firstItem.length >= 5) firstItem else "!" + firstItem + "!"
}.toUpperCase()
println("First item after modifications: '$modifiedFirstItem'")
with
一个非扩展函数:上下文对象作为参数传递,但是在 lambda 表达式内部,它可以作为接收者(this)使用。 返回值是lambda 表达式结果。
建议使用 with 来调用上下文对象上的函数,而不使用 lambda 表达式结果。 在代码中,with 可以理解为“对于这个对象,执行以下操作。”
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
with 的另一个使用场景是引入一个辅助对象,其属性或函数将用于计算一个值。
val numbers = mutableListOf("one", "two", "three")
val firstAndLast = with(numbers) {
"The first element is ${first()}," +
" the last element is ${last()}"
}
println(firstAndLast)
run
上下文对象 作为 接收者(this)来访问。 返回值 是 lambda 表达式结果。
run 和 with 做同样的事情,但是调用方式和 let 一样——作为上下文对象的扩展函数.
当 lambda 表达式同时包含对象初始化和返回值的计算时,run 很有用。
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
// 同样的代码如果用 let() 函数来写:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}
除了在接收者对象上调用 run 之外,还可以将其用作非扩展函数。 非扩展 run 可以使你在需要表达式的地方执行一个由多个语句组成的块。
val hexNumberRegex = run {
val digits = "0-9"
val hexDigits = "A-Fa-f"
val sign = "+-"
Regex("[$sign]?[$digits$hexDigits]+")
}
for (match in hexNumberRegex.findAll("+1234 -FFFF not-a-number")) {
println(match.value)
}
apply
上下文对象 作为 接收者(this)来访问。返回值 是上下文对象本身。
对于不返回值且主要在接收者(this)对象的成员上运行的代码块使用 apply。apply 的常见情况是对象配置。这样的调用可以理解为“将以下赋值操作应用于对象”。
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
将接收者作为返回值,可以轻松地将 apply 包含到调用链中以进行更复杂的处理。
also
上下文对象作为 lambda 表达式的参数(it)来访问。 返回值 是上下文对象本身。
also 对于执行一些将上下文对象作为参数的操作很有用。 对于需要引用对象而不是其属性与函数的操作,或者不想屏蔽来自外部作用域的 this 引用时,请使用 also。
当在代码中看到 also 时,可以将其理解为“并且用该对象执行以下操作”。
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
总结
下表总结了Kotlin作用域函数的主要区别与使用场景:
函数 | 对象引用 | 返回值 | 是否是扩展函数 | 使用场景 |
---|
let | it | Lambda 表达式结果 | 是 | 1. 对一个非空对象执行 lambda 表达式
2. 将表达式作为变量引入为局部作用域中 |
run | this | Lambda 表达式结果 | 是 | 对象配置并且计算结果 |
run | - | Lambda 表达式结果 | 不是:调用无需上下文对象 | 在需要表达式的地方运行语句 |
with | this | Lambda 表达式结果 | 不是:把上下文对象当做参数 | 一个对象的一组函数调用 |
apply | this | 上下文对象 | 是 | 对象配置 |
also | it | 上下文对象 | 是 | 附加效果 |
关于如何辨别Kotlin的作用域函数就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。