1. 程式人生 > >Kotlin run、with、apply、also、let、takeIf、takeUnless、repeat的使用及區別

Kotlin run、with、apply、also、let、takeIf、takeUnless、repeat的使用及區別

文章目錄


網上有關於run、with、apply、also、let、takeIf、takeUnless、repeat的使用及區別並且已經總結好了,但通過死記硬背用不了多久我們就會忘記,而且由於這幾個函式比較像,我們很容易弄混了,下面我們通過分析原始碼的角度去學習這個函式的使用及區別。下面我們以run來舉例:
在這裡插入圖片描述

下面1、2、3對應上圖紅色方框1、2、3處
1、T.run 表示:此函式是擴充套件函式,任意物件都可呼叫。
2、接收一個函式,並且函式內任意呼叫改物件的屬性、方法
3、返回函式內的return

我們對這幾處的標記都清楚後我們具體看看run、with、apply、also、let、takeIf、takeUnless、repeat的功能。

run

/**
 * Calls the specified function [block] and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

/**
 * Calls the specified function [block] with `this` value as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

run和T.run的用法如下:

	val person = Person()
        person.run { 
            age = 1
            sex = 3
        }

        var e = run{
            var a =""
            2+3
        }

class Person(var name: String = "", var age: Int = 0, var sex: Int = 0)

T.run是擴充套件函式,任何物件都可以呼叫,大括號內可以直接呼叫物件的屬性和方法。
run:返回大括號內最後一行
注:有的文章中說可以使用return返回,如下:

 var e = run{
            var a =""
            return 2+3
        }

但我這無法編譯通過,有可能是版本的問題。

with

/**
 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

我們發現with也不是擴充套件函式,而且有2個引數,返回block函式的返回值
用法:

with(person) {
            age = 1
            this.sex = 3
        }

1、block函式內this代表with的引數,所以可以直接呼叫persion的屬性、方法
2、從用法上我們發現persion不能為null
3、this可以省略

apply

public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

1、apply返回的是物件本身
2、block函式內this代表物件本身

使用:

val newPersion = person.apply { this.age = 0}
                .apply { sex =2 }

是不是感覺可以替代Builder模式

also

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

和apply對比發現是block的引數不一樣,apply是T.(),also是(T),所以使用是不同的:

val b = person.also {it.age =4}
                .also { it.sex = 5 }

also中不可以直接呼叫物件的屬性和方法,通過it呼叫,it代表當前物件

let

@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

和also對比不同是返回值不同,also返回當前物件,let返回block函式最後一行

takeif

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (predicate(this)) this else null
}

如果函式返回值是true則返回物件本身,否則返回null
使用:

var p = person.takeIf { true }

takeUnless

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (!predicate(this)) this else null
}

takeUnless和takeif相反,使用:

var p = person.takeUnless { true }

repeat

@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
    contract { callsInPlace(action) }

    for (index in 0 until times) {
        action(index)
    }
}

就是封裝了一個for迴圈,使用:

repeat(4){
            //it代表 0..3
            print(it)
        }

總結

看完上面的是不是特別暈,感覺沒什麼區別啊,下面總結下他們的區別和相同點,我覺得沒必要背下來,我們需要通過原始碼理解他的意思。

1、他們都是作用域函式,都提供了內部作用域
2、函式前沒有T的是普通函式(run,with)和有T的是擴充套件函式(T.run,T.also 等)
3、引數不同

block:T.() 引數當前物件,this可以省略
blcok:(T): 引數當前物件,it表示當前物件,不可省略
無引數的形式:block: ()

4、返回值不同:

return block():返回block函式最後一行
return this:返回當前物件

使用場景

在這裡插入圖片描述

具體程式碼中的使用

1、屬性設定(包括自定義類和系統物件)

原來的寫法:

var person = Person()
        person.age = 1
        person.name = ""
        person.sex = 4

let寫法 :

person.let {
            it.name = ""
            it.age = 1
            it.sex = 3
        }

run寫法(推薦):

person.run {
            name = ""
            age = 1
            sex = 3
        }

系統屬性:

	textView.run { 
            textSize = 12F
            text = ""
        }

是不是run的寫法更“漂亮”。

2、需要null判斷的地方

if(null != textView){
            print("textView is not null")
            add()
        }

run、let寫法:

textView?.let {
            print("textView is not null")
            add()
        }
        textView?.run {
            print("textView is not null")
            add()
        }