[译] 掌握 Kotlin 中的标准库函数 _ run、with、let,搞懂开源框架设计思想真的这么重要吗
this?.databaseEnabled = true}}// Nice.(比较好的实现方式)webview.settings?.run {javaScriptEnabled = truedatabaseEnabled = true}
在这种情况下,显然 T.run 扩展函数更好,因为我们可以在使用它之前对可空性进行检查。
2、this VS it 参数(This vs. it argument)
如果我们对比 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 是可以被省略的。因此我们上面的示例中,我们可以在 println 语句中直接使用 $length 而不是 ${this.lenght}. 所以我把这个称之为传递 this 参数
然而对于 T.let 函数的声明,你将会注意到 T.let 是传递它自己本身到函数中 block: (T)。因此这个类似于传递一个 lambda 表达式作为参数。它可以在函数作用域内部使用 it 来指代. 所以我把这个称之为传递 it 参数
从上面看,似乎 T.run 比 T.let 更加优越,因为它更隐含,但是 T.let 函数具有一些微妙的优势,如下所示:
1、T.let 函数提供了一种更清晰的区分方式去使用给定的变量函数/成员与外部类函数/成员。
2、例如当 it 作为函数的参数传递时,this 不能被省略,并且 it 写起来比 this 更简洁,更清晰。
3、T.let 允许更好地命名已转换的已使用变量,即可以将 it 转换为其他有含义名称,而 T.run 则不能,内部只能用 this 指代或者省略。
stringVariable?.let {nonNullString ->println("The non null string is $nonNullString")}
3、返回 this VS 其他类型 (Return this vs. other type)
现在,让我们看看 T.let 和 T.also,如果我们看看它的内部函数作用域,它们都是相同的。
stringVariable?.let {println("The length of this String is {it.length}")}// Exactly the same as belowstringVariable?.also {println("The length of this String is {it.length}")}
然而,他们微妙的不同在于他们的返回值 T.let 返回一个不同类型的值,而 T.also 返回 T 类型本身,即这个。
这两个函数对于函数的链式调用都很有用,其中 T.let 让您演变操作,而 T.also 则让您对相同的变量执行操作。
简单的例子如下:
val original = "abc"// Evolve the value and send to the next chainoriginal.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 chainoriginal.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 似乎看上去没有意义,因为我们可以很容易地将它们组合成一个功能块。仔细思考,它有一些很好的优点。
1、它可以对相同的对象提供非常清晰的分离
过程,即创建更小的函数部分。
2、在使用之前,它可以非常强大的进行自我操作,从而实现整个链式代码的构建操作。
当两者结合在一起使用时,即一个自身演变,一个自我保留,它能使一些操作变得更加强大。
// Normal approachfun makeDir(path: String): File {val result = File(path)result.mkdirs()return result}// Improved approachfun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }
回顾所有属性特征
通过回顾这 3 个属性特征,我们可以非常清楚函数的行为。让我来说明 T.apply 函数,由于我并没有以上函数中提到过它。 T.apply 的三个属性如下
1、它是一个扩展函数
2、它是传递 this 作为参数
3、它是返回 this (即它自己本身)
因此,使用它,可以想象下它可以被用作:
// Normal approachfun createInstance(args: Bundle) : MyFragment {val fragment = MyFragment()fragment.arguments = argsreturn fragment}// Improved approachfun createInstance(args: Bundle)= MyFragment().apply { arguments = args }
或者我们也可以让无链对象创建链式调用。
// Normal approachfun createIntent(intentData: String, intentAction: String): Intent {val intent = Intent()intent.action = intentActionintent.data=Uri.parse(intentData)return intent}// Improved approach, chainingfun createIntent(intentData: String, intentAction: String) =Intent().apply { action = intentAction }
评论