美文网首页
Kotlin标准库 — — apply/also/run/let

Kotlin标准库 — — apply/also/run/let

作者: 吳下阿蒙 | 来源:发表于2018-08-27 08:23 被阅读0次

    Kotlin标准库中的apple/also/run/let这四个函数相当有意思。它们的实现非常简单,区区两三行,却直击了Java的若干痛点。

    Kotlin对它们的定位是scope functions。这是什么意思呢?我理解,scope functions是指这些函数可以通过新的作用域操作对象,避免引入临时变量或一次性函数污染原作用域


    首先,让我们来看看apply:

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

    寥寥数行,信息量却很大。

    此函数有如下几个特点:

    1. 使用inline:跟C的inline类似,Kotlin的inline也是在编译期将函数调用直接在调用处展开,以节省函数调用的开销;所不同的是,Kotlin还会把传入的lambda函数也展开。
    2. 结合泛型和扩展,即T.apply,使得它成为了任意对象的成员函数
    3. 唯一的参数block是一个lambda函数,且block的参数是T.(),相当于为T扩展了一个无参的成员函数
    4. 由于applyblock都相当于是T的成员函数,在这两个函数的作用域里的this都指的是T的一个实例
    5. 执行传入的block,并返回this

    apply如何使用呢?一个例子:

    class House {
        val window = Window().apply {
            location = "Living Room"
            color = Color.WHITE
            size = Size.LARGE
        }
    }
    

    这个例子写成Java将会是这样:

    class House {
        private final Window window = createWindow();
    
        private Window createWindow() {
            Window window = new Window();
            setLocation("Living Room");
            setColor(Color.WHITE);
            setSize(Size.LARGE);
            return window;
        }
    }
    

    可以看出,利用apply我们避免了引入createWindow这种一次性使用的函数污染House的成员。

    (当然,对于上面的例子,我们可以通过给Window加一个Builder来避免引入额外的函数createWindow。但如果我们还需要调用setter以外的方法呢? )


    下面,我们来看看also

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

    诶?alsoapply不是一样的吗?非也。这两个函数对block的定义不同。之前的apply中,block的定义是T.() -> Unit,可作为T的扩展函数;而also的是(T) -> Unit,它没有扩展T,而是把T作为一个参数。

    also适用于需要把T传递给其它对象的函数的场景。例如:

    fun newHouseWithAWindow() { 
        return House()
            .add(Window())
            .also { log.debug("Created a new house with a window. house=$it") }
    }
    

    而用Java写的话,我们为了能够log返回值,需要引入一个局部变量:

    Window newHouseWithAWindow() {
        House house = new House().add(new Window());
        log.debug("Created a new house with a window. {}", house);
        return house;
    }
    

    最后是runlet

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

    runapply类似,letalso类似。所不同的是,这两个函数在执行完block之后,返回的是block的返回值,而非this

    总而言之,这四个函数的异同点在于:

    1. 传入的block的作用域里的是this还是it
    2. 返回值是this还是block的执行结果
    函数 block里 返回值
    apply this this
    also it this
    run this block的结果
    let it block的结果

    那么,怎么选择应该用哪个函数呢?Kotlin官方文档Coding Conventions中有一节Using scope functions apply/with/run/also/let对此已有解答,这里不再赘述。

    相关文章

      网友评论

          本文标题:Kotlin标准库 — — apply/also/run/let

          本文链接:https://www.haomeiwen.com/subject/ohaaiftx.html