天天看點

kotlin中标準函數的使用(with、also、aply、let、run)一、簡介二、let三、with三、run四、apply五、also六、 run的兩種方式為什麼this作用域不一樣,一個是 調用該函數所在的執行個體,一個是T對象本身執行個體七、為什麼let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身八、五個方法對比和比較

目錄

一、簡介

二、let

三、with

三、run

四、apply

五、also

六、 run的兩種方式為什麼this作用域不一樣,一個是 調用該函數所在的執行個體,一個是T對象本身執行個體

七、為什麼let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身

八、五個方法對比和比較

一、簡介

Standard.kt是Kotlin庫的一部分,它定義了一些基本函數,主要包括五個常用方法:run、with、apply、also、let

二、let

看下 let的源碼

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

定義了一個T類型對象,可以調用let方法,參數傳了一個lambda函數,該參數block為 T類型參數,傳回值是R類型, 然後fun let 傳回值類型也是R。

根據lamda文法特點,可以用it代指對象本身,傳回值為函數塊的最後一行或指定return表達式。

總結就是 使用方式為

(1) 不可為空變量

var t:T = T()
t.let{
  it.a()
  it.b()
  it.c()
}
           

(2) 可為空變量

var t:T? = null
t?.let{
 it.a()
 it.b()
 it.c()
}
           

(3) 傳回值 為代碼塊最後一句 或者指定 return

@JvmStatic
    fun main(args: Array<String>) {
        val result = "let".let {
            println(it.length)
            "let"
        }
        println(result)
    }

運作結果
3
let
           

(4)最常用的是 可空變量 多個操作處理,不用判斷空異常

(5)特點:let函數 用it代表調用對象本身,不能使用this或省略,傳回值為函數體最後一句或者return指定傳回

三、with

看下with源碼:

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

 與let不同的是,let定義的是  T.let, 這裡沒有定義 T.with,而是定義with,傳參是 T對象本身 和 lamda函數 block,根據lamda簡化規則,方法最後一個參數是lamda表達式,且唯一,可以把表達式拿出去。是以一般使用如下

with(loginData){
            println(this?.token)
            println(this?.userId)
            println(this?.userName)
        }
           

可以調用this,這裡 loginData是可空變量,如果非可空,還可以省略this,是以with有個缺點就是如果傳入對象為可空對象比較麻煩,沒有空處理,後面也是lamda表達式和let一樣 傳回值也是 函數體最後一句或者是return指定

(1)使用場景:用于調用同一個類的多個方法時,如果比較懶連it都不想寫,可以省去類名重複,直接調用類的方法就行,經常用于擴充卡中,例如RecycleView的onBinderViewHolder中。

代碼示例:

orderInfo?:return
        with(orderInfo){
            helper.setText(R.id.tv_order_total_money, orderPrice.toString())
            helper.setText(R.id.tv_order_total_discounts_money, presentPrice.toString())
            helper.setText(R.id.tv_order_total_real_money, actualPrice.toString())
        }
           

(2) 特點:with函數需要傳對象, 用this或省略代表對象本身,沒有做空異常處理,傳回值為函數體最後一句或者return指定傳回

三、run

看下run的源碼

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

@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 代表作用對象為T類型對象。

 第一個方法:

var name = "123"
        println(name)
        run{
            name = "234"
            println(name)
        }
        println(name)

結果是:
123
234
234
           

第二個方法

var loginData:LoginData?=null
        loginData?.let {
            it.token = "1jdifjidfjidfj"
            it.userId = "1001"
            it.userName = "123"
        }
        loginData?.run {
            token = "1jdifjidfjidfj"
            userId = "1001"
            userName = "123"
        }
           

run可以像let一樣有使用空判斷

run結合了 let和with兩者特點,是以 能用let 和 with寫的都能用 run寫,run也是可以用this和省略代指調用對象本身,可以省略,然後傳回值類型也是函數體 最後一句 或retrun指定

(1)使用場景:let和with使用的所有場景

(2)特點:run函數是let和with的結合體, 用this或省略代表對象本身,可以做空異常處理,傳回值為函數體最後一句或者return指定傳回

四、apply

看源碼:

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

可以看到 最後有指定傳回值 this,傳回本身,是以apply和 上面let、with、run 在傳回上面有明顯差別,然後傳參是一個 無傳回值lambda表達式,表達式和run表達式類似,也是用this或者省略代指本身。

(1)使用場景:

需要對對象本身做一些操作,然後傳回對象本身,對象執行個體初始化,需要對對象屬性初始化指派,或者動态inflate出一個XML的View的時候需要給View綁定資料也會用到,這種情景非常常見。

bottomView = View.inflate(activity, R.layout.view_bottom, null).apply{
   tv_title = "123"
   tv_name = "djfidfj"
   seek_bar.max = 100
   seek_bar.progress = 0
}
           

需要做多級判空處理:

kotlin中标準函數的使用(with、also、aply、let、run)一、簡介二、let三、with三、run四、apply五、also六、 run的兩種方式為什麼this作用域不一樣,一個是 調用該函數所在的執行個體,一個是T對象本身執行個體七、為什麼let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身八、五個方法對比和比較

(2)總結:大體與run相似,也是this或者省略代表本身,可以做空異常處理,傳回值為本身,可以做鍊式調用處理

五、also

 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
}
           

我們發現also和let很相似,最後都是用it代表對象本身,隻不過傳回值不一樣,let是傳回函數體内最後一行的值,如果最後一行為空就傳回一個Unit類型的預設值。also是傳回本身

kotlin中标準函數的使用(with、also、aply、let、run)一、簡介二、let三、with三、run四、apply五、also六、 run的兩種方式為什麼this作用域不一樣,一個是 調用該函數所在的執行個體,一個是T對象本身執行個體七、為什麼let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身八、五個方法對比和比較

(1)使用場景:多個擴充函數鍊式調用

(2)總結:大體與let相似,也是it代表本身,可以做空異常處理,傳回值為本身,可以做鍊式調用處理

六、 run的兩種方式為什麼this作用域不一樣,一個是 調用該函數所在的執行個體,一個是T對象本身執行個體

我們來看看兩種方式源碼有啥差別:

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

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

這裡可以看到 一個是() ->R, 一個是 T.() ->R,

是以回調函數裡面 this一個是代表調用run方法的執行個體,

另一個函數裡面this作用域是 類型T的執行個體

七、為什麼let 和 also使用it代替本身,而 with、run和apply 使用this 或省略代表本身

我們來看看源碼差別:

let和also 中lambda表達式是:
block: (T) -> R 和 block: (T) -> Unit
前面都是傳 (T)
而with、run和apply中的lambda表達式是:
 block: T.() -> R、block: T.() -> R和 block: T.() -> Unit
前面都是傳 T.()

一個是傳(T),一個是傳T.()這個差別
           

 隻有T.() 這樣才代表 回調方法裡面this作用域為 T對象本身,而this又可以省略。而(T) 這樣 回調方法裡面的不能用this代表T對象本身,是以用一個t指代 T對象本身

八、五個方法對比和比較

名稱 源碼 函數體内什麼代表本身 傳回值 是否是擴充函數 适用的場景
let
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}      
it 閉包形式傳回 适用于處理不為null的操作場景
with
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}      
this或省略 閉包形式傳回 适用于調用同一個類的多個方法時,可以省去類名重複,直接調用類的方法即可,經常用于Android中RecyclerView中onBinderViewHolder中,資料model的屬性映射到UI上
run
(1)
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
      
(2)
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}      
this或省略 閉包形式傳回 适用于let,with函數任何場景。
apply
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}      
this或省略 傳回this

1、适用于run函數的任何場景,一般用于初始化一個對象執行個體的時候,操作對象屬性,并最終傳回這個對象。

2、動态inflate出一個XML的View的時候需要給View綁定資料也會用到.

3、一般可用于多個擴充函數鍊式調用

4、資料model多層級包裹判空處理的問題

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
}      
it 傳回this 适用于let函數的任何場景,一般可用于多個擴充函數鍊式調用

參考文檔:https://blog.csdn.net/u013064109/article/details/78786646

繼續閱讀