美文网首页
Kotlin基础知识六: 标准函数(Standard Funct

Kotlin基础知识六: 标准函数(Standard Funct

作者: 北雁南飞_8854 | 来源:发表于2020-05-06 20:28 被阅读0次

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()

相关文章

网友评论

      本文标题:Kotlin基础知识六: 标准函数(Standard Funct

      本文链接:https://www.haomeiwen.com/subject/nyrgghtx.html