细说Kotlin工具函数及使用准则—函数let()run()apply()with()also()
Posted LQS_Android
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了细说Kotlin工具函数及使用准则—函数let()run()apply()with()also()相关的知识,希望对你有一定的参考价值。
作⽤域函数
Person("Alice", 20, "Amsterdam").let {
println(it)
it.moveTo("London")
it.incrementAge()
println(it)
}
如果不使⽤ let 来写这段代码,就必须引⼊⼀个新变量,并在每次使⽤它时重复其名称。
val alice = Person("Alice", 20, "Amsterdam")
println(alice)
alice.moveTo("London")
alice.incrementAge()
println(alice)
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}")
}
}
val adam = Person("Adam").apply {
age = 20 // 和 this.age = 20 或者 adam.age = 20 ⼀样
city = "London"
}
println(adam)
反过来,let 及 also 将上下⽂对象作为 lambda 表达式参数。如果没有指定参数名,对象可以⽤隐式默认名称 it 访问。it ⽐ this 简短,带有 it 的表达式通常更容易阅读。然⽽,当调⽤对象函数或属性时,不能像this 这样隐式地访问对象。因此,当上下⽂对象在作⽤域中主要⽤作函数调⽤中的参数时,使⽤ it 作为上下⽂对象会更好。若在代码块中使⽤多个变量,则 it 也更好。
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()
返回值
根据返回结果,作⽤域函数可以分为以下两类:
val numberList = mutableListOf<Double>()
numberList.also { println("Populating the list") }.apply {
add(2.71)
add(3.14)
add(1.06)
}.also { println("Sorting the list") }
.sort()
它们还可以⽤在返回上下⽂对象的函数的 return 语句中。
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
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")
}
⼏个函数
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 表达式结果。
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函数
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函数
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
将接收者作为返回值,你可以轻松地将 apply 包含到调⽤链中以进⾏更复杂的处理。
val numbers = mutableListOf("one", "two", "three")
numbers.also { println("The list elements before adding new one: $it") }.add("four")
总结了这么多,为了帮助你选择合适的作⽤域函数,我们提供了它们之间的主要区别表。
以下是根据预期⽬的选择作⽤域函数的简短指南:
val number = Random.nextInt(100)
val evenOrNull = number.takeIf { it % 2 == 0 }
val oddOrNull = number.takeUnless { it % 2 == 0 }
println("even: $evenOrNull, odd: $oddOrNull")
当在 takeIf 及 takeUnless 之后链式调⽤其他函数,不要忘记执⾏空检查或安全调⽤( ?. ),因为他们的 返回值是可为空的。
val str = "Hello"
val caps = str.takeIf { it.isNotEmpty() }?.toUpperCase()
//val caps = str.takeIf { it.isNotEmpty() }.toUpperCase() // 编译错误
println(caps)
takeIf 及 takeUnless 与作⽤域函数⼀起特别有⽤。⼀个很好的例⼦是⽤ let 链接它们,以便在与给定谓词匹配的对象上运⾏代码块。为此,请在对象上调⽤ takeIf ,然后通过安全调⽤( ?. )调⽤ let 。对于与谓词不匹配的对象,takeIf 返回 null,并且不调⽤ let 。
fun displaySubstringPosition(input: String, sub: String) {
input.indexOf(sub).takeIf { it >= 0 }?.let {
println("The substring $sub is found in $input.")
println("Its start position is $it.")
}
}
displaySubstringPosition("010000011", "11")
displaySubstringPosition("010000011", "12")
没有标准库函数时,相同的函数看起来是这样的:
fun displaySubstringPosition(input: String, sub: String) {
val index = input.indexOf(sub)
if (index >= 0) {
println("The substring $sub is found in $input.")
println("Its start position is $index.")
}
}
displaySubstringPosition("010000011", "11")
displaySubstringPosition("010000011", "12")
函数式编程常用API及解释:
函数式编程常见的转换函数map()、过滤函数filter()、聚合函数reduce()请参见下一篇:
细说Kotlin工具函数及使用准则-转换函数map()、过滤函数filter()、聚合函数(二)
用知识铭记编程的岁月!遇见即是缘分,愿你我的脚步轻盈地踏过编程的花海!
以上是关于细说Kotlin工具函数及使用准则—函数let()run()apply()with()also()的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin差异化分析,let,run,with,apply及also
Kotlin差异化分析,let,run,with,apply及also
Kotlin篇差异化分析,let,run,with,apply及also