Standard Functions
1. apply
/* Calls the specified function block with this value as its receiver and returns this value. */
inline fun <T> T.apply(block: T.() -> Unit): T
apply函数可以理解为是一种配置函数。它允许你在receiver上调用一系列的函数、而不需要在每个函数前都加上变量名。当lambda执行完成后,apply()函数返回调用者的实例。
举例:
fun main(args: Array<String>) {
val menuFile = File("menu-file.txt").apply {
setReadable(true) //menuFile.setReadable(true)
setWritable(true) //menuFile.setWritable(true)
setExecutable(false) //menuFile.setExecutable(false)
}
//匿名函数的写法:
val menuFile2 = File("menu-file.txt").apply(fun File.() {
println("menuFile this: ${this.absoluteFile}")
setReadable(true) //menuFile.setReadable(true)
setWritable(true) //menuFile.setWritable(true)
setExecutable(false) //menuFile.setExecutable(false)
})
}
2. let
/* Calls the specified function block with this value as its argument and returns its result. */
inline fun <T, R> T.let(block: (T) -> R): R
let扩展函数的实际上是一个作用域函数,当你需要去定义一个变量在一个特定的作用域范围内,let函数的是一个不错的选择;let函数另一个作用就是可以避免写一些判断null的操作。
举例:
val firstItemSquared = listOf(1,2,3).last().let {
it * it
}
//匿名函数的写法:
val firstItemSquared2 = listOf(1,2,3).last().let(fun(it: Int): Int {
return it * it
})
println("fistItemSquared: $firstItemSquared") //9
避免判空的写法:
fun formatGreeting(vipGuest: String?): String {
return vipGuest?.let {
"Welcome, $it. Please, go straight back - your table is ready."
} ?: "Welcome to the tavern. You'll be seated soon."
}
println(formatGreeting(null)) //Welcome to the tavern. You'll be seated soon.
总结一: apply与let的相同点和不同点:
apply接收的函数类型为() -> Unit,即无参、无返回值,匿名函数执行结束后,会返回当前的receiver;然而let接收的函数类型为(T) → R,其中T为调用者类型,R为匿名函数中返回的特定类型,或lambda表达式最后一行返回的类型。
3. run
/* Calls the specified function block and returns its result. */
inline fun <R> run(block: () -> R): R
/* Calls the specified function block with this value as its receiver and returns its result. */
inline fun <T, R> T.run(block: T.() -> R): R
run函数与apply类似,不同点是run的返回值是lambda的结果,然而apply的返回值是调用者,即receiver。
val menuFile = File("menu-file.txt")
val servesDragonsBreath = menuFile.run {
readText().contains("Dragon's Breath")
}
//匿名函数的写法:
val servesDragonsBreath2 = menuFile.run(fun File.(): Boolean {
return readText().contains("Dragon's Breath")
})
println(servesDragonsBreath)
run还可以用来在receiver上执行一个function reference。
fun main(args: Array<String>) {
fun nameIsLong(name: String) = name.length >= 20
"Madrigal".run(::nameIsLong) // False
"Polarcubis, Supreme Master of NyetHack".run(::nameIsLong) // True
}
为什么要使用run函数?
使用run函数实现链式调用(chained calls)比嵌套的函数调用更容易阅读和跟踪。
例如,判断名字是否大于10个字符、并根据结果生成格式化消息、然后打印出来。
//名字是否大于10个字符
fun nameIsLong(name: String) = name.length >= 10
//根据参数生成消息
fun playerCreateMessage(nameTooLong: Boolean): String {
return if (nameTooLong) {
"Name is too long. Please choose another name."
} else {
"Welcome, adventurer"
}
}
如果使用嵌套调用时:
println(playerCreateMessage(nameIsLong("Polarcubis, Supreme Master of NyetHack")))
如果使用run函数链式调用:
"Polarcubis, Supreme Master of NyetHack"
.run(::nameIsLong)
.run(::playerCreateMessage)
.run(::println)
run函数也可以脱离receiver而使用:
val status = run {
if (healthPoints == 100) "perfect health" else "has injuries"
}
4. with
/* Calls the specified function block with the given receiver as its receiver and returns its result. */
inline fun <T, R> with(receiver: T, block: T.() -> R): R
目的:用于对同一个对象执行多次操作而不需要反复把对象的名称写出来。
perform multiple operations on the same object without repeating its name.
考虑以下例子:在result实例上调用不同的方法时,每次都需要重复result这个名字。
fun alphabet(): String {
val result = StringBuilder()
for (letter in 'A'..'Z') {
result.append(letter)
}
result.append("\nNow I know the alphabet!")
return result.toString()
}
with函数有两个参数,第一个参数为任意类型的对象实例,第二个参数是一个lambda表达式。with函数会把第一个参数转换为lambda表达式的接受者。可以通过显式的this指针调用接受者的方法。
使用with函数重写:
fun alphabet(): String {
val stringBuilder = StringBuilder()
return with(stringBuilder) {
for (letter in 'A'..'Z') {
this.append(letter)
}
append("\nNow I know the alphabet!")
this.toString()
}
}
进一步简化的写法:
fun alphabet() = with(StringBuilder()) {
for (letter in 'A'..'Z') {
append(letter)
}
append("\nNow I know the alphabet!")
toString()
}
5. also
/* Calls the specified function block with this value as its argument and returns this value. */
inline fun <T> T.also(block: (T) -> Unit): T
also与let非常相似,都会把receiver作为参数传递给lambda,不同点是also会返回receiver,而let返回的是lambda的结果。
因为also函数会返回receiver本身,所以可以在同一receiver上执行also的链式调用。
举例:
fun main(args: Array<String>) {
var fileContents: List<String>
val contents = File("file.txt")
.also {
print(it.name)
}.also {
fileContents = it.readLines()
}
println("fileContents: $fileContents")
}
6. takeIf
/* Returns this value if it satisfies the given predicate or null, if it doesn't. */
inline fun <T> T.takeIf(predicate: (T) -> Boolean): T?
takeIf会执行一个断言(predicate)函数,如果断言函数返回true,takeIf返回receiver本身;如果返回false,则takeIf返回null。
fun main(args: Array<String>) {
val fileContents = File("myfile.txt")
.takeIf { it.canRead() && it.canWrite() }
?.readText()
println("fileContents: $fileContents") //fileContents: null
}
如果不使用takeIf的写法:
fun main(args: Array<String>) {
val file = File("myfile.txt")
val fileContents = if (file.canRead() && file.canWrite()) {
file.readText()
} else {
null
}
println("fileContents: $fileContents") //fileContents: null
}
可见,takeIf不需要临时变量file,也不需要专门处理返回null的情景。
7. takeUnless
/* Returns this value if it does not satisfy the given predicate or null, if it does. */
inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T?
如果断言函数返回false时,返回this值;否则,返回null。
举例,当一个文件不是隐藏文件时,读取出文件内容:
val fileContents = File("myfile.txt")
.takeUnless { it.isHidden }
?.readText()
网友评论