目錄
一、簡介
二、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
}
需要做多級判空處理:
(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是傳回本身
(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 | | it | 閉包形式傳回 | 是 | 适用于處理不為null的操作場景 |
with | | this或省略 | 閉包形式傳回 | 否 | 适用于調用同一個類的多個方法時,可以省去類名重複,直接調用類的方法即可,經常用于Android中RecyclerView中onBinderViewHolder中,資料model的屬性映射到UI上 |
run | (2) | this或省略 | 閉包形式傳回 | 是 | 适用于let,with函數任何場景。 |
apply | | this或省略 | 傳回this | 是 | 1、适用于run函數的任何場景,一般用于初始化一個對象執行個體的時候,操作對象屬性,并最終傳回這個對象。 2、動态inflate出一個XML的View的時候需要給View綁定資料也會用到. 3、一般可用于多個擴充函數鍊式調用 4、資料model多層級包裹判空處理的問題 |
also | | it | 傳回this | 是 | 适用于let函數的任何場景,一般可用于多個擴充函數鍊式調用 |
參考文檔:https://blog.csdn.net/u013064109/article/details/78786646