聊聊kotlin中的run, let, with, apply, also ...

导语: kotlin 标准库中, run, let, with, apply, also 实现解读

在看kotlin代码时, 可能会看到let, run这样的函数调用, 最早接触的时候, 我也弄不清楚其中差异, 觉得用法这么像, 为什么弄出多个名字, 不过仔细看看嘛, 还是略有不同的, 待我慢慢道来.

kotlin中, run, let, with, apply, also, 都是标准库的函数, 它的实现可以直接在IDE上跳转看到, 也可以在这里看: Standard.kt

看这些函数的实现, 会发现它们非常短, 最多不超过两行.. 我们一个一个看过去吧

方法解读

run函数(两个定义):

- public inline fun  run(block: () -> R): R = block()
- public inline fun  T.run(block: T.() -> R): R = block()

第一个函数run, 接受一个参数, 参数类型是签名”() -> R”的函数, 这里也被称为block. 执行的结果就是执行这个block, 并返回R类型的对象. 这里的上下文this和run()方法被调用时候的this一致.

第二个函数, 看起来和第一个函数非常像, 但是多了一个泛型参数T.., 它是kotlin的extension的一个用法:

确切说, 这个run是一个generic extension函数, 对于任何类型T, 扩展了run这个方法, 参数类型是签名”T.() -> R”的函数.. 这个函数的上下文this和第一个run不同, 上下文对应T这个类型的实例this. 关于kotlin extension的更详细介绍, 可以查看官网文档

this上下文差异是这两个run用法最大的不同

let函数

- public inline fun  T.let(block: (T) -> R): R = block(this)

let extension函数看起来是不是有点像是两个run函数的混合? 为什么说是混合:

  1. let的函数参数block中, 对应的上下文this和第一个run函数是一致的
  2. let和第二个run函数一样是一个extension函数, 但是它的block参数支持一个参数, 就是任意类型T的实例, 在extension函数定义时候, “this”就对应扩展类型实例.

所以使用let函数时, block可以同时访问两个this上下文差异

with函数

- public inline fun  with(receiver: T, block: T.() -> R): R = receiver.block()

with函数不是一个extension函数, 它第二个参数是一个函数类型, 所以可以用with(x) { BLOCK } 的写法. 这个BLOCK和run函数的extension函数版本的block参数, 是等价的.

apply函数, also函数

- public inline fun  T.apply(block: T.() -> Unit): T { block(); return this }
- public inline fun  T.also(block: (T) -> Unit): T { block(this); return this }

剩下apply和also两个函数, 他们和run, let的实现又非常相似 apply和T.run的block参数的上下文一致 also和T.let的block参数的上下文一致 只是返回值和run/let有所区别.

示例代码

解读完了, 来一段示例代码

import org.junit.Test

/**
 * Created by vashzhong on 2017/7/27.
 */
class TestDemo {
    val str: String = "str(class val)"

    @Test fun playWithStandard() {
        val str = "str(function val)"
        var number = 0
        var ret = 0

        println("run:")
        ret = run {
            number++
            println("str = $str")
            println("this = $this")
            println("this.str = ${this.str}")
            0
        }
        println("ret = $ret, number = $number")

        println("T.run:")
        ret = number.run {
            number++
            println("str = $str")
            println("this = $this")
            1
        }
        println("ret = $ret, number = $number")

        println("T.let:")
        ret = number.let {
            println("str = $str")
            println("it = $it")
            println("this = $this")
            println("this.str = ${this.str}")
            2
        }
        println("ret = $ret, number = $number")

        println("with(T):")
        ret = with(number) {
            number++
            println("str = $str")
            println("this = $this")
            3
        }
        println("ret = $ret, number = $number")

        println("apply:")
        ret = number.apply {
            number++
            println("str = $str")
            println("this = $this")
            4
        }
        println("ret = $ret, number = $number")

        println("also:")
        ret = number.also {
            number++
            println("str = $str")
            println("it = $it")
            println("this = $this")
            println("this.str = ${this.str}")
            5
        }
        println("ret = $ret, number = $number")
    }
}

理解它的输出, 应该就对run, let, with, apply, also的差异完全明了

思考

  1. 在上边的实例代码中, T.run, T.apply, with(T)对应的block中, 怎么拿到TestDemo实例里边的str这个String? “str(class val)”
  2. 为什么T.run对应的block中, number++了, 但是println(“this = $this”)输出结果没有增加?
  3. 有什么办法看到和kotlin代码等价的java代码?

参考链接

  • The difference between Kotlin’s functions, KM的md语法链接解析有bug, 不支持url里边带@,直接提出来不链接了 “@tpolansk” )/the-difference-between-kotlins-functions-let-apply-with-run-and-else-ca51a4c696b8`
  • Kotlin Basics: Standard Extension Functions

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JarvanMo的IT专栏

Dart In Action -Dart快速入门(三)

本文基本上是将dart官网部分内容进行翻译,没兴趣的请出门左转至Dart的官网,有兴趣的同志请继续阅读本文。 Flutter教程在这里

381
来自专栏芋道源码1024

深入解析Java反射(1) - 基础

> https://www.sczyh30.com/posts/Java/java-reflection-1/ ? 排版有点点崩嘿

564
来自专栏coding for love

JS入门难点解析6-作用域链

(注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)

621
来自专栏诸葛青云的专栏

C语言什么是结构体?初步学习C语言结构体三部曲

结构体部分内容,涉及结构体定义,结构体变量,结构体指针,结构体数组,更多内容敬请关注。

763
来自专栏快乐八哥

JavaScript内置对象--Math对象

在JavaScript开发中,除了简单加减乘除运算之外,有时候开发,特别是动画或者游戏开发中,需要使用复杂的数学运算。JavaScript中Math对象提供了一...

2055
来自专栏walterlv - 吕毅的博客

Roslyn 语法树中的各种语法节点及每个节点的含义

2018-07-18 12:24

771
来自专栏柠檬先生

JavaScript 基础(七) 箭头函数 generator Date JSON

ES6 标准新增了一种新的函数: Arrow Function(箭头函数)。     x => x *x     上面的箭头相当于:   ...

1925
来自专栏超然的博客

es6 Object.assign

  Object.assign方法用来将源对象(source)的所有可枚举属性,复制到目标对象(target)。它至少需要两个对象作为参数,第一个参数是目标对象...

1183
来自专栏破晓之歌

python 模板实现-引擎的编写(有时间试一下)

1.模板的编写:https://blog.csdn.net/MageeLen/article/details/68920913

853
来自专栏前端桃园

通俗的方式理解动态类型,静态类型;强类型,弱类型

1134

扫码关注云+社区