掌握Kotlin标准函数:run, with, let, also and apply,这一篇就够了

掌握Kotlin标准函数:run, with, let, also and apply

Kotlin的一些标准函数非常相似,我们不确定使用哪个函数。在这里我将介绍一个简单的方法来清楚地区分他们的差异和如何选择使用。

范围函数

我重点关注 run, with, T.run, T.let, T.also and T.apply函数。我称他们为范围函数,因为我认为他们的主要功能是为调用函数提供一个内部范围。

run函数是说明最简单的范围方法

fun test() {
    var mood = "I am sad"

    run {
        val mood = "I am happy"
        println(mood) // I am happy
    }
    println(mood)  // I am sad
}

有了这个函数,在 test函数内部,你可以有一个单独的范围, mood在重新定义为 I am happy并打印之前,它被完全封闭在 run范围内。

这个范围函数本身似乎不是很有用。但是相比范围,还有一点不错的是,它返回范围内最后一个对象。

因此,下面代码将是很纯洁的,我们可以像下面一样,将 show()方法应用到两个 view,而不是 调用两次。

run {
      if (firstTimeView) introView else normalView
    }.show()

范围函数的3个属性

为了使范围函数更有趣,让我用3个属性将他们的行为分类,并且使用这些属性来区分它们。

1.正常vs.扩展函数

如果我们看看定义,with并且T.run这两个函数实际上非常相似。下面示例实现功能是一样的。

with(webview.settings) {
    javaScriptEnabled = true
    databaseEnabled = true
}
// 相似
webview.settings.run {
    javaScriptEnabled = true
    databaseEnabled = true
}


然而,它们的不同之处在于 with是正常函数,而 T.run是扩展函数。

那么问题是,每个的优点是什么?

想象一下,如果 webview.settings可能是空的,那么看起来就像下面一样了。

with(webview.settings) {
      this?.javaScriptEnabled = true
      this?.databaseEnabled = true
   }
}

webview.settings?.run {
    javaScriptEnabled = true
    databaseEnabled = true
}

在这种情况下,显然 T.run扩展功能比较好,因为在使用之前我们可以判空。

2.This vs. it参数

如果我们看看定义,, T.run并且 T.let这两个函数除了接受参数的方式不一样外几乎是一样的。以下两个函数的逻辑是相同的。

stringVariable?.run {
      println("The length of this String is $length")
}

stringVariable?.let {
      println("The length of this String is ${it.length}")
}

如果你检查 T.run函数签名,你会注意到 T.run只是作为扩展函数调用 block: T.()。因此,所有的范围内, T可以被称为 this。在编程中, this大部分时间可以省略。因此,在我们上面的例子中,我们可以在 println声明中使用 $length,而不是 ${this.length}。我把这称为传递 this参数

然而,对于 T.let函数签名,你会注意到 T.let把自己作为参数传递进去,即 block: (T)。因此,这就像传递一个lambda参数。它可以在作用域范围内使用 it作为引用。所以我把这称为传递 it参数

从上面看,它似乎 T.run是更优越,因为 T.let更隐含,但是这是 T.let函数有一些微妙的优势如下:

  • T.let相比外部类函数/成员,使用给定的变量函数/成员提供了更清晰的区分
  • this不能被省略的情况下,例如当它作为函数的参数被传递时 itthis更短,更清晰。
  • T.let允许使用更好的变量命名,你可以转换 it为其他名称。
stringVariable?.let {
      nonNullString ->
      println("The non null string is $nonNullString")
}

3.返回当前类型 vs.其他类型

现在,我们来看看T.letT.also,如果我们看它们的内部函数范围,使用起来是一样的

stringVariable?.let {
      println("The length of this String is ${it.length}")
}
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"
// 改变值并且传递到下一链条
original.let {
    println("The original String is $it") // "abc"
    it.reversed() // 改变参数并且传递到下一链条
}.let {
    println("The reverse String is $it") // "cba"
    it.length   // 改变类型
}.let {
    println("The length of the String is $it") // 3
}
// 错误
// 在链中发送相同的值(打印的答案是错误的)
original.also {
    println("The original String is $it") // "abc"
    it.reversed() // 即使我们改变它,也是没用的
}.also {
    println("The reverse String is ${it}") // "abc"
    it.length  // 即使我们改变它,也是没用的
}.also {
    println("The length of the String is ${it}") // "abc"
}

// also通过修改原始字符串也可以达到同样目的
// 在链中发送相同的值
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. 在使用之前,它可以实现非常强大的自我操纵,实现链条建设者操作(builder 模式)。

当两者结合在一起时,即一个自我演变,一个自我保留,可以变得非常强大,例如下面

// 正常方法
fun makeDir(path: String): File  {
    val result = File(path)
    result.mkdirs()
    return result
}
// 改进方法
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }


所有的属性

通过说明这3个属性,我们应该可以了解这些函数的行为了。让我们再来看看 T.apply函数,因为上面没有提到。这3个属性在 T.apply定义如下...

  1. 这是一个扩展函数
  2. this作为参数传递。
  3. 它返回this(即它本身)

因此,可以想象,它可以像下面一样被使用

// 正常方法
fun createInstance(args: Bundle) : MyFragment {
    val fragment = MyFragment()
    fragment.arguments = args
    return fragment
}
// 改进方法
fun createInstance(args: Bundle) 
              = MyFragment().apply { arguments = args }


或者我们也可以创建链式调用。

// 正常方法
fun createIntent(intentData: String, intentAction: String): Intent {
    val intent = Intent()
    intent.action = intentAction
    intent.data=Uri.parse(intentData)
    return intent
}
//  改进实现
fun createIntent(intentData: String, intentAction: String) =
        Intent().apply { action = intentAction }
                .apply { data = Uri.parse(intentData) }


函数选择

因此,显然,有了这三个属性,我们现在可以对上述函数进行相应的分类。在此基础上,我们可以在下面形成一个决策树,可以帮助我们决定使用哪个函数。

image

希望上面的决策树可以清晰说明函数区别,也简化您的决策,使您能够恰当掌握这些函数的使用。

(每天学习一点点.每天进步一点点,分享不宜路过点个赞呀,喜欢的点个关注后续更新不断)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容