Kotlin基础——作用域函数

Kotlin中的作用域函数

Kotlin 标准库包含几个函数,它们的唯一目的是在对象的上下文中执行代码块。当对一个对象调用这样的函数并提供一个 lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数。共有以下五种:let、run、with、apply 以及 also。
这些函数基本上做了同样的事情:在一个对象上执行一个代码块。不同的是这个对象在块中如何使用,以及整个表达式的结果是什么。
以上内容来自《Kotlin中国》文档中对作用域函数的解释。

let 函数

函数原型:

public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}
复制代码

特点:
1.内联扩展函数。
2.上下文对象作为 lambda 表达式的参数(it)来访问。
3.返回值是 lambda 表达式的结果。

用法:

结合Kotlin空指针安全检验,基于let函数的特性,函数型参就是调用者本身,所以只要在函数外部做空安全校验后函数内部使用型参就不再需要进行空安全校验了。

举个栗子:

val sb: StringBuilder? = null
// 只有在调用函数的时候需要进行空安全校验
sb?.let{
   // 函数内部不需要进行空安全校验
   it.append("a")
   it.append("b")
   it.append("c")
}
复制代码

with 函数

函数原型:

public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
复制代码

特点:
1.内联非扩展函数。
2.上下文对象作为参数传递,但是在 lambda 表达式内部,它可以作为接收者(this)使用。
3.返回值是 lambda 表达式结果。

用法:

使用同一个对象执行多步操作时。

val sb = StringBuilder()
// 消除sb.xxx()的模版代码,更加简洁直观
with(sb){
    append("a")
    append("b")
    append("c")
}
复制代码

引入一个辅助对象,其属性或函数将用于计算一个值。

val sb = StringBuilder()
val str = with(sb){
    append("a")
    append("b")
    append("c")
    toString()
}
复制代码

run 函数

函数原型:

  • 作为扩展函数的run函数:

    public inline fun <T, R> T.run(block: T.() -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return block()
    }
    复制代码

    特点:
    1.内联扩展函数。
    2.上下文对象 作为接收者(this)来访问。
    3.返回值是 lambda 表达式结果。

    用法:

    当 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函数:

    public inline fun <R> run(block: () -> R): R {
        contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return block()
    }
    复制代码

    特点:
    1.内联函数。
    2.没有上下文对象。
    3.返回值是 lambda 表达式的结果。

    用法:

    执行有多个语句的代码块,且需要代码块的返回值。

    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 函数

函数原型:

public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
复制代码

特点:
1.内联扩展函数。
2.上下文对象 作为接收者(this)来访问。
3.返回值 是上下文对象本身。

用法:

对于不返回值且主要在接收者(this)对象的成员上运行的代码块使用 apply。apply 的常见情况是对象配置。这样的调用可以理解为“将以下赋值操作应用于对象”。

val adam = Person("Adam").apply {
    age = 32
    city = "London"        
}
println(adam)
复制代码

also 函数

函数原型:

public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}
复制代码

特点:
1.内联扩展函数。
2.上下文对象作为 lambda 表达式的参数(it)来访问。
3.返回值是上下文对象本身。

用法:

also 对于执行一些将上下文对象作为参数的操作很有用。 对于需要引用对象而不是其属性与函数的操作,或者不想屏蔽来自外部作用域的 this 引用时,请使用 also。

当你在代码中看到 also 时,可以将其理解为“并且用该对象执行以下操作”。

val numbers = mutableListOf("one", "two", "three")
numbers
    .also { println("The list elements before adding new one: $it") }
    .add("four")
复制代码

如何选用作用域函数

函数 对象引用 返回值 是否是扩展函数
let

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享