08. 标准库函数

Kotlin 标准库里有一些支持 lambda 的通用工具类标准函数。本章,我们会学习 apply、let、 run、with、also 和 takeIf 这六个常用标准函数。标准函数简化代码的威力。它们不仅能让代码简洁易读。通过示例看看如何使用它们。

这一章还会用到一种叫接收者(receiver)的类型实例。这是因为,Kotlin 的标准函数本质上 都是扩展函数(extension function),而接收者是跟扩展函数相关的术语。扩展(extension)很灵 活,方便定义用于各种类型的函数,我们将在之后的章节深入学习它。

let

Kotlin 标准库函数 let 可用于范围界定和 null 检查。当对一个对象调用时,let 执行给定的代码块并返回其最后一个表达式的结果。对象可以在块内通过引用(默认情况下)或自定义名称访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
val empty = "test".let {
customPrint(it)
it.isEmpty()
}
println(" is empty: $empty")


fun printNonNull(str: String?) {
println("Printing \"$str\":")

str?.let {
print("\t")
customPrint(it)
println()
}
}

fun printIfBothNonNull(strOne: String?, strTwo: String?) {
strOne?.let { firstString ->
strTwo?.let { secondString ->
customPrint("$firstString : $secondString")
println()
}
}
}

printNonNull(null)
printNonNull("my string")
printIfBothNonNull("First","Second")

run

run 和 apply 差不多。但与 apply 不同,run 函数不返回接收者。不过,与 apply 不同,run 返回的是 lambda 结果,也就是 true 或 false 值。

和 let 一样,run 是标准库中的另一个范围函数。基本上,它执行相同的操作: 执行一个代码块并返回其结果。不同之处在于,在 run 中,对象是通过以下命令访问的。当您想要调用对象的方法而不是将其作为参数传递时,这非常有用。

1
2
3
4
5
6
7
8
9
10
11
fun getNullableLength(ns: String?) {
println("for \"$ns\":")
ns?.run {
println("\tis empty? " + isEmpty())
println("\tlength = $length")
length
}
}
getNullableLength(null)
getNullableLength("")
getNullableLength("some string with Kotlin")

with

with 函数是 run 的变体。它们的功能行为是一样的,但 with 的调用方式不同。和之前介绍 的标准函数都不一样,调用 with 时需要值参作为其第一个参数传入。

With 是一个非扩展函数,可以简洁地访问其参数的成员: 在引用其成员时可以省略实例名称。

1
2
3
4
5
6
with(configuration) {
println("$host:$port")
}

// instead of:
println("${configuration.host}:${configuration.port}")

apply

apply 函数可看作一个配置函数:你可以传入一个接收者, 然后调用一系列函数来配置它以便使用。如果提供 lambda 给 apply 函数执行,它会返回配置好的接收者。

1
2
3
4
5
6
val jake = Person()
val stringDescription = jake.apply {
name = "Jake"
age = 30
about = "Android developer"
}.toString()

also

also 函数和 let 函数功能相似。和 let 一样,also 也是把接收者作为值参传给 lambda。但 有一点不同:also 返回接收者对象,而 let 返回 lambda 结果。

1
2
3
4
val jake = Person("Jake", 30, "Android developer")
.also {
writeCreationLog(it)
}

takeIf

takeIf 函数需要判断 lambda 中提供的条件表达式(叫 predicate),给出 true 或 false 结果。如果判断结果是 true,从 takeIf 函数返回接收者对象;如果是 false,则返回 null。

下面这段代码逻辑是,当且仅当文件可读且可写时,才读取文件内容。

1
2
3
val fileContents = File("myfile.txt")
.takeIf { it.canRead() && it.canWrite() }
?.readText()

takeUnless

takeUnless 和 takeIf 唯一的区别是:只有判断你给定的条件结果是 false 时, takeUnless 才会返回原始接收者对象。

建议你尽量不要用 takeUnless,尤其是有很多复杂的条件要判断的时候,因为要读懂理清代码比较费时。

总结

参考