[译]掌握Kotlin中的标准库函数_ run、with、let、also和apply

在这种情况下,显然 T.run 扩展函数更好,因为我们可以在使用它之前对可空性进行检查。

2、this VS it 参数(This vs. it argument)

如果我们对比 T.runT.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语句中直接使用 l e n g t h ∗ ∗ 而不是 ∗ ∗ length** 而不是 ** length而不是{this.lenght}. 所以我把这个称之为传递 this参数

然而对于 T.let 函数的声明,你将会注意到 T.let 是传递它自己本身到函数中block: (T)。因此这个类似于传递一个lambda表达式作为参数。它可以在函数作用域内部使用it来指代. 所以我把这个称之为传递 it参数

从上面看,似乎T.runT.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.letT.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类型本身,即这个。

这两个函数对于函数的链式调用都很有用,其中T.let让您演变操作,而T.also则让您对相同的变量执行操作。

简单的例子如下:

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似乎看上去没有意义,因为我们可以很容易地将它们组合成一个功能块。仔细思考,它有一些很好的优点。

  • 1、它可以对相同的对象提供非常清晰的分离过程,即创建更小的函数部分。
  • 2、在使用之前,它可以非常强大的进行自我操作,从而实现整个链式代码的构建操作。

当两者结合在一起使用时,即一个自身演变,一个自我保留,它能使一些操作变得更加强大。

// 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函数,由于我并没有以上函数中提到过它。 T.apply的三个属性如下

  • 1、它是一个扩展函数
  • 2、它是传递this作为参数
  • 3、它是返回 this (即它自己本身)

因此,使用它,可以想象下它可以被用作:

// 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个属性特征,我们现在可以对功能进行相应的分类。基于此,我们可以在下面构建一个决策树,以帮助确定我们想要使用哪个函数,来选择我们需要的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

希望上面的决策树能够更清晰地说明功能,并简化你的决策,使你能够适当掌握这些功能的使用.

译者有话说

  • 1、为什么我要翻译这篇博客?

我们都知道在Kotlin中Standard.Kt文件中短短不到100来行库函数源码,但是它们作用是非常强大,可以说它们是贯穿于整个Kotlin开发编码过程中。使用它们能让你的代码会更具有可读性、更优雅、更简洁。善于合理使用标准库函数,也是衡量你对Kotlin掌握程度标准之一,因为你去看一些开源Kotlin源码,随处可见的都是使用各种标准库函数。

但是这些库函数有难点在于它们的用法都非常相似,有的人甚至认为有的库函数都是多余的,其实不然,每个库函数都是有它的实际应用场景。虽然有时候你能用一种库函数也能实现相同的功能,但是也许那并不是最好的实现方式。相信很多初学者对于这些标准库函数也是傻傻分不清楚(曾经的我也是),但是这篇博客非常一点在于它提取出了这些库函数三个主要特征:是否是扩展函数、是否传递this或it做为参数(在函数内部表现就是this和it的指代)、是否需要返回调用者对象本身,基于特征就可以进行分类,分类后相应的应用场景也就一目了然。这种善于提取特征思路还是值得学习的。

  • 2、关于使用标准库函数需要补充的几点。
    第一点: 建议尽量不要使用多个标准库函数进行嵌套,不要为了简化而去做简化,否则整个代码可读性会大大降低,一会是it指代,一会又是this指代,估计隔一段时间后连你自己都不知道指代什么了。
    第二点: 针对上面译文的let函数和run函数需要补充下,他们之所以能够返回其他类型的值,其原理在于内部block lambda表达式返回的R类型,也就是这两者函数的返回值类型取决于传入block lambda表达式返回类型,然而决定block lambda表达式返回值类型,取决于外部传入lambda表达式体内最后一行返回值
    第三点: 关于T.also和T.apply函数为什么都能返回自己本身,是因为在各自Lambda表达式内部最后一行都调用return this,返回它们自己本身,这个this能被指代调用者,是因为它们都是扩展函数特性
  • 3、总结
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

评论里面有些同学有疑问关于如何学习material design控件,我的建议是去GitHub搜,有很多同行给的例子,这些栗子足够入门。

有朋友说要是动真格的话,需要NDK以及JVM等的知识,首现**NDK并不是神秘的东西,**你跟着官方的步骤走一遍就知道什么回事了,无非就是一些代码格式以及原生/JAVA内存交互,进阶一点的有原生/JAVA线程交互,线程交互确实有点蛋疼,但平常避免用就好了,再说对于初学者来说关心NDK干嘛,据鄙人以前的经历,只在音视频通信和一个嵌入式信号处理(离线)的两个项目中用过,嵌入式信号处理是JAVA->NDK->.SO->MATLAB这样调用的我原来MATLAB的代码,其他的大多就用在游戏上了吧,一般的互联网公司会有人给你公司的SO包的。
至于JVM,该掌握的那部分,相信我,你会掌握的,不该你掌握的,有那些专门研究JVM的人来做,不如省省心有空看看计算机系统,编译原理。

一句话,平常多写多练,这是最基本的程序员的素质,尽量挤时间,读理论基础书籍,JVM不是未来30年唯一的虚拟机,JAVA也不一定再风靡未来30年工业界,其他的系统和语言也会雨后春笋冒出来,但你理论扎实会让你很快理解学会一个语言或者框架,你平常写的多会让你很快熟练的将新学的东西应用到实际中。
初学者,一句话,多练。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

再风靡未来30年工业界,其他的系统和语言也会雨后春笋冒出来,但你理论扎实会让你很快理解学会一个语言或者框架,你平常写的多会让你很快熟练的将新学的东西应用到实际中。**
初学者,一句话,多练。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值