最近我仔细研究了Kotlin的作用域函数,发现有时候它们需要一个扩展函数作为参数,就以also和apply为例:
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
从消费者的Angular 来看,这两者似乎没有太大的区别,我可以用几乎相同的方式称呼它们:
fun main() {
val logger = Logger.getLogger("Test")
logger.info("A").apply {
logger.info("B")
}.also {
logger.info("C")
}
}
所以我想知道我在这里遗漏了什么?在Kotlin中是否有一个模式或最佳实践,何时在高阶函数中使用扩展函数,以及它的确切好处是什么?
2条答案
按热度按时间31moq8wy1#
从消费者的Angular 来看,这两者似乎没有太大的区别,我可以用几乎相同的方式来称呼它们
这是因为你没有利用这里的接收器或参数,你使用的是现有的变量
logger
,而不是接收器apply
提供的任何变量。作用域函数的一个要点是,当你还没有变量时,可以在表达式中使用它们:
正如你在上面的例子中可能注意到的,接受lambda和receiver的高阶函数将以不同的方式提供值给函数的调用者。在块中,如果是receiver,参数将以
this
的形式可用,否则将以参数的形式可用。byqmnocz2#
在
Logger.info
的情况下,它们确实没有太大的不同。info
返回Unit
,所以T
是Unit
,而Unit
是一个相当糟糕的例子。考虑一个更好的例子:
这两个语句都做同样的事情,即下面的语句,但是在一个语句中:
但是因为
apply
的lambda是一个“扩展函数”,或者换句话说,Person
是一个 * 接收者 *,所以你不需要任何额外的限定条件(比如it.
或person.
等)来引用Person
的成员。你可以简单地通过它们的 * 简单名称 * 来引用它们,比如“firstName
“。这就是为什么
apply
经常被用来设置对象的属性,或者将更改应用到对象,因为它很方便,不必多次编写it.
。另一方面,
also
的lambda参数有Person
作为参数,所以要引用它的成员,需要it.xxx
、it.yyy
等。it
引用lambda的唯一参数。这使得
also
适合于对表达式的结果执行附加操作,因为说“是很自然的,而also
对it
“执行此操作。