有很多关于标准库函数apply
/ with
/ run
/ also
/ {的用法的博文(如this) {1}}可以让你更容易确定何时实际使用哪些漂亮的功能。
几周以来,官方文档甚至最终提供了有关该主题的指南:https://kotlinlang.org/docs/reference/coding-conventions.html#using-scope-functions-applywithrunalsolet
尽管如此,我认为用函数名称记住函数的个别用例非常困难。我的意思是,对我来说,它们似乎是可以互换的,为什么let
不是let
?{/ p>
有什么建议吗?我认为这些名字并不是非常富有表现力,因此最初很难看出这些差异。
答案 0 :(得分:22)
以下是非官方概述这些名称的概况。
let
的灵感来自功能编程世界。根据{{3}}
“let”表达式将函数定义与受限范围
相关联
在像Haskell这样的FP语言中,您可以使用let
将值绑定到受限范围内的变量,如此
aaa = let y = 1+2
z = 4+6
in y+z
Kotlin中的等效代码(尽管过于复杂)将是
fun aaa() = (1+2).let { y ->
(4+6).let { z ->
y + z
}
}
let
的典型用法是将某些计算的结果绑定到范围而不“污染”外部范围。
creater.createObject().let {
if (it.isCorrect && it.shouldBeLogged) {
logger.log(it)
}
}
// `it` is out of scope here
Wikipedia功能的灵感来自with
或with
等语言的myObject.colour := clRed;
myObject.size := 23.5;
myObject.name := 'Fred';
语言构造(可能还有许多其他语言)
with关键字是Delphi提供的便于引用的便利 复杂变量的元素,例如记录或对象。
with myObject do begin colour := clRed; size := 23.5; name := 'Fred'; end;
可以改写:
with(myObject) { color = clRed size = 23.5 name = "Fred" }
等效的Kotlin将是
build
Delphi添加到stdlib。您可以在2015年看到Visual Basic问题,其中用户要求提供此类功能,甚至建议稍后使用的名称为“apply”。
在问题apply
和this中,您可以看到有关命名的讨论。提出了init
和apply
等替代方案,但由Daniil Vodopian提出的名称apply
最终获胜。
with
与apply
类似,因为它可用于初始化构造函数之外的对象。这就是为什么,在我看来,with
也可能被命名为with
。但是,当apply
首先添加到stdlib时,Kotlin开发人员决定不破坏现有代码并以不同的名称添加它。
具有讽刺意味的是,Xtend语言提供了所谓的https://youtrack.jetbrains.com/issue/KT-6903,它与apply
基本相同。
https://youtrack.jetbrains.com/issue/KT-6094被添加到stdlib甚至晚于apply
,即版本1.1。同样,with-operator =>
包含讨论。该函数基本上类似于(T) -> Unit
,除了它采用常规lambda T.() -> Unit
而不是扩展lambda val object = creater.createObject().also { it.initiliaze() }
。
在提议的名称中有“applyIt”,“applyLet”,“on”,“tap”,“touch”,“peek”,“make”。但“也”赢了,因为它不会与任何关键字或其他stdlib函数碰撞,而且它的用法(或多或少)会像英语句子一样读取。
实施例
fun <R> run(block: () -> R): R
读取有点像
创建者,创建对象,也初始化它!
其他stdlib函数的用法读起来有点像英语句子,包括also
和https://youtrack.jetbrains.com/issue/KT-6903,它们也在1.1版本中添加。
最后,takeIf
函数实际上有两个签名。第一个val logger = run {
val name = System.property("logger_name")
Logger.create(name)
}
只需要一个lambda而运行。它主要用于将lambda表达式的结果赋值给顶级属性
fun <T, R> T.run(block: T.() -> R): R
第二个签名val result = myObject.run {
intitialize()
computeResult()
}
是一个扩展函数,它将扩展lambda作为参数,并且出于对称原因似乎也被命名为“run”。它还“运行”lambda但在扩展接收器的上下文中
{{1}}
我不知道命名的任何历史原因。
答案 1 :(得分:8)
我强烈建议您阅读此blog以了解所有这些范围功能。
这些博客的一些关键:
在每个字母的第一个字母后面,您会得到首字母缩略词“LARA”。
常见用例
。通过强>
with()
在功能上与run()
的扩展函数版本相同,因此非常适合Initialize and execute的用例。更多information。
答案 2 :(得分:7)
添加到@kirillRakhman答案:
命名过程中的一个主要部分是(仍然是)主要用例的流利阅读经验。
with
:
with(database) {
open()
send()
close()
}
apply
:
val v = View().apply {
width = 3.0
height = 4.0
register(this)
}
also
:
db.users()
.filter { it.age > 18 }
.map { account }
.also { log(it) }
恕我直言,它与let
无法真正合作。毕竟,它取自“那些可怕的FP语言”。但我经常将其视为一种Let's do this!
构造。如下所示,您可以将代码读作let's print it!
:
account.map { it.owner }.sumBy {age}.let { print(it) }