翻译说明:java
原标题: Mastering Kotlin standard functions: run, with, let, also and applygit
原文地址: medium.com/@elye.proje…github
原文做者: Elyeweb
Kotlin 有一些类似的标准函数,咱们不肯定该使用哪一种函数。接下来,我将介绍一种简单的方法来清楚地区分它们的差别以及如何选择使用。编程
我会专一于函数的 run,with,T.run,T.let,T.also 和 T.apply。我称他们为做用域函数,我将它们的主要功能视为:为其调用者函数提供的内部做用域。bash
说明范围中最简单方法是运行函数app
fun test() {
var mood = "I am sad"
run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}
复制代码
有了这个,在测试函数内部,你能够有一个单独的范围,在打印以前 mood 从新定义 I am happy,而且它彻底包含在运行范围内。less
这个做用域函数自己彷佛不是颇有用。但它有另外一个好处,而不只仅是范围;它返回一些东西,即范围内的最后一个对象。函数
所以,下面将是整洁的,咱们能够将 show() 两个视图应用于下面,而不是两次调用它。post
run {
if (firstTimeView) introView else normalView
}.show()
复制代码
为了使范围函数更有趣,让我用3个属性对它们的行为进行分类。我将使用这些属性来区分它们。
若是咱们看一下 with 和 T.run,这两个函数其实是至关相似的。如下是一样的事情。
with(webview.settings) {
javaScriptEnabled = true
databaseEnabled = true
}
// similarly
webview.settings.run {
javaScriptEnabled = true
databaseEnabled = true
}
复制代码
然而,它们的不一样之处在于一个是标准函数,即 with 另外一个是扩展函数 T.run。
因此问题是,每一个的优点是什么?
想象一下,若是 webview.settings 可能为 null,它们将以下所示。
// Yack!
with(webview.settings) {
this?.javaScriptEnabled = true
this?.databaseEnabled = true
}
// Nice.
webview.settings?.run {
javaScriptEnabled = true
databaseEnabled = true
}
复制代码
在这种状况下,明确的 T.run 扩展函数更好,由于咱们能够在使用以前应用检查可空性。
If we look at T.run and T.let, both functions are similar except for one thing, the way they accept the argument. The below shows the same logic for both functions.
若是咱们看一下 T.run 和 T.let,除了一件事俩个函数都是类似的,他们接受实际参数的方式相似。如下显示了俩种逻辑相同的函数。
stringVariable?.run {
println("The length of this String is $length")
}
// Similarly.
stringVariable?.let {
println("The length of this String is ${it.length}")
}
复制代码
若是你检查 T.run 函数签名,你会发现 T.run 它只是做为扩展函数调用 block: T.()。所以,在范围内,T 能够称为 this。在编程中,this 大多数时候能够省略。所以,在上面的示例中,咱们能够 {length} 在 println 语句中使用,而不是 {this.length}。我称之为发送此做为参数。
可是对于 T.let 函数签名,您会注意到它 T.let 正在将自身发送到函数中,即 block: (T)。所以,这就像发送它的 lambda 实际参数。它能够在范围函数中引用为 it。因此我称之为发送它做为实际参数。
从上面看,它彷佛 T.run 更优越,T.let 由于它更隐含,但功能有一些微妙的优势 T.let 以下: -
所述 T.let 确实提供更清晰的区分使用给定的变量函数/构件与外部类功能/部件
在 this 不能省略的状况下,例如当它做为函数的参数发送时 it,写入比写入 this 更短而且更清晰。
在 T.let 容许转换使用变量的命名更好,你便可以转换 it 为其余名称。
stringVariable?.let {
nonNullString ->
println("The non null string is $nonNullString")
}
复制代码
如今,让咱们来看看 T.let 和 T.also,二者是相同的,若是咱们看看它的内部函数范围。
stringVariable?.let {
println("The length of this String is ${it.length}")
}
// Exactly the same as below
stringVariable?.also {
println("The length of this String is ${it.length}")
}
复制代码
然而,他们微妙的不一样就是他们的返回。所述 T.let 返回不一样类型的值,而 T.also 返回 T 自己即 this。
二者都对连接函数颇有用,其中 T.let 让你进化操做,而后 T.also 让你对同一个变量执行即 this。
简单说明以下
val original = "abc"
// Evolve the value and send to the next chain
original.let {
println("The original String is $it") // "abc"
it.reversed() // evolve it as parameter to send to next let
}.let {
println("The reverse String is $it") // "cba"
it.length // can be evolve to other type
}.let {
println("The length of the String is $it") // 3
}
// Wrong
// Same value is sent in the chain (printed answer is wrong)
original.also {
println("The original String is $it") // "abc"
it.reversed() // even if we evolve it, it is useless
}.also {
println("The reverse String is ${it}") // "abc"
it.length // even if we evolve it, it is useless
}.also {
println("The length of the String is ${it}") // "abc"
}
// Corrected for also (i.e. manipulate as original string
// Same value is sent in the chain
original.also {
println("The original String is $it") // "abc"
}.also {
println("The reverse String is ${it.reversed()}") // "cba"
}.also {
println("The length of the String is ${it.length}") // 3
}
复制代码
在 T.also 彷佛毫无心义上面,由于咱们能够很容易地将它们组合成一个单独的函数。仔细思考,它有一些很好的优点。
它能够在相同的物体上提供很是清晰的分离过程,即制做较小的函数部分。
在使用以前,它能够很是强大的自我操做,使连接构建器操做。
当俩者结合起来是,一个继续演化,一个保持不变,它变得更强,例以下面
// Normal approach
fun makeDir(path: String): File {
val result = File(path)
result.mkdirs()
return result
}
// Improved approach
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }
复制代码
经过查看3个属性,咱们几乎能够知道函数行为。让我说明一下这个 T.apply 函数,由于它没有在上面提到过。3个属性 T.apply 以下......
所以,使用它,能够想象,它能够用做
// Normal approach
fun createInstance(args: Bundle) : MyFragment {
val fragment = MyFragment()
fragment.arguments = args
return fragment
}
// Improved approach
fun createInstance(args: Bundle)
= MyFragment().apply { arguments = args }
复制代码
或者咱们也能够建立链式对象。
// Normal approach
fun createIntent(intentData: String, intentAction: String): Intent {
val intent = Intent()
intent.action = intentAction
intent.data=Uri.parse(intentData)
return intent
}
// Improved approach, chaining
fun createIntent(intentData: String, intentAction: String) =
Intent().apply { action = intentAction }
.apply { data = Uri.parse(intentData) }
复制代码
所以很明显,有了3个属性,咱们如今能够相应地对函数进行分类。基于此,咱们能够在下面造成一个决策树,能够帮助决定咱们想要使用的功能等待咱们须要的功能。
但愿上面的决策树可以更清楚地说明函数,并简化您的决策,使您可以恰当地掌握这些函数的使用。
随意提供一些很好的实例,说明如何使用这些函数做为对此博客的回应。我很乐意听到你的消息。这可能有益于其余人。
我但愿你感谢这篇文章,它对你有所帮助。与他人分享。
你能够在这里查看个人其余有趣话题。
在 Medium, Twitter 或Facebook上关注我,获取有关Android,Kotlin等相关主题的小技巧和知识。〜Elye〜
欢迎关注 Kotlin 中文社区!
中文官网:www.kotlincn.net/
中文官方博客:www.kotliner.cn/
公众号:Kotlin
知乎专栏:Kotlin
CSDN:Kotlin中文社区
掘金:Kotlin中文社区
简书:Kotlin中文社区