日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Kotlin 中的 run、let、with、apply、also、takeIf、takeUnless 语法糖使用和原理分析

發(fā)布時間:2025/3/21 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Kotlin 中的 run、let、with、apply、also、takeIf、takeUnless 语法糖使用和原理分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

這些Kotlin的語法糖函數(shù)經(jīng)常用,但也很容易搞混,所以轉(zhuǎn)載一下,若混了可以回來再看

轉(zhuǎn)載自公眾號:紙上淺談

?

正文:

在 Kotlin 有一些可以簡化代碼的語法糖,比如 run、let、with、apply、also、takeIf、takeUnless ?等。

再不明白這些語法糖的情況下去看 Kotlin 代碼就會一臉懵逼,可當(dāng)明白之后就會覺得原來可以這樣簡化。

帶接收者的函數(shù)字面值

使用這些語法糖之前回顧一下 Kotlin 的函數(shù)式編程,在分析 Kotlin 使用 Anko 構(gòu)建布局 文章中有提到?帶接收者的函數(shù)字面值

它的形式是這樣的:

1//?定義一個類 2class?ReceiveObject? 3//?定義一個函數(shù) 4fun?exec(invoke:?ReceiveObject.()->?Int){}

在 Kotlin 中,函數(shù)也可以當(dāng)做變量傳參,例如:

1fun?funAsArg(args:()->Int){} 2//?調(diào)用 3funAsArg?{?2?}

args?是變量名,它的類型就是函數(shù),函數(shù)形式在變量名后面約定:()->Int,函數(shù)沒有參數(shù),但是會返回一個 Int 類型的值。

而帶接收者的函數(shù)字面值,就是在作為傳入?yún)?shù)的函數(shù)變量的具體函數(shù)形式的參數(shù)前面多了接收者對象,簡單說就是在?()前面多了一個點和一個對象,成了如下的形式:

1fun?exec(invoke:?ReceiveObject.()->?Int){}

就是這多了的一個點和一個對象,讓它有了不一樣的功能。

簡單的說,invoke 變量是一個函數(shù)作為變量,需要傳遞一個具體函數(shù)實現(xiàn)作為形參給 invoke,那么在具體函數(shù)實現(xiàn)里面就可以調(diào)用接收者對象 ReceiveObject 的相關(guān)方法,如下:

1????//?接收者對象,有個?show?方法 2????class?ReceiveObject{ 3????????fun?show(){ 4????????????println("call") 5????????} 6????} 7????//?具體函數(shù)實現(xiàn) 8????val?invoke:?ReceiveObject.()?->?Int?=?{ 9????????this.show()?//?用?this?指代?接收者對象?ReceiveObject 10????????2 11????} 12????exec(invoke)

如上,在 invoke 方法里面使用?this?指代 ReceiveObject 對象,可以調(diào)用它的方法。

而 invoke 變量是作為參數(shù)傳遞給 exec 函數(shù)的,如果 exec 函數(shù)為空,那么 inkoke 具體實現(xiàn)的 show 方法也不會被調(diào)用的,在 exec 中調(diào)用 invoke 的方法如下:

1fun?exec(invoke:?ReceiveObject.()?->?Int){ 2????val?receObj?=?ReceiveObject() 3????//?兩種調(diào)用形式 4????//?類似于?ReceiceObject?拓展函數(shù)一樣的調(diào)用 5????receObj.invoke()? 6????//?把?ReceiceObject?作為參數(shù)傳遞給?invoke?調(diào)用 7????invoke(receObj) 8}

在 exec 的具體調(diào)用中,我們需要構(gòu)造一個 ReceiveObject 對象實例,不然怎么去調(diào)用它的 show 方法呢。

在上面的例子中,還需要構(gòu)造一個指定的接收者對象實例才能完成 invoke 的調(diào)用,而 Kotlin 的語法糖中還有一種叫做?拓展函數(shù)。

拓展函數(shù)

拓展函數(shù)相當(dāng)于給某個類添加函數(shù),但這個函數(shù)并不屬于這個類的函數(shù),和 static 方法是兩碼事。

1????fun?Context.showToast(msg:?String)?{ 2????????Toast.makeText(this,?msg,?Toast.LENGTH_SHORT).show() 3????}

在拓展函數(shù)中,使用 this 指代被拓展的類實例,上面代碼中 this 指代就是 Context 。

有了 拓展函數(shù)和帶接收者的函數(shù)字面值,就可以實現(xiàn)文章標(biāo)題提到的那些語法糖了。

例如,針對 ReceiveObject 對象添加它的拓展函數(shù),拓展函數(shù)的參數(shù)又是一個函數(shù),函數(shù)是帶接收者的函數(shù)字面值,這個接收者對象就是 ReceiveObject 對象它本身,這樣調(diào)用 invoke 方法就不用再構(gòu)造 ReceiveObject 對象了。

1fun?ReceiveObject.exec(invoke:?ReceiveObject.()?->?Int){ 2????invoke() 3}

語法糖

下面介紹的語法糖都是位于 Kotlin Standard.kt 文件中的。

run 語法糖

run 的語法糖有兩種:

1public?inline?fun?<R>?run(block:?()?->?R):?R?{ 2????contract?{ 3????????callsInPlace(block,?InvocationKind.EXACTLY_ONCE) 4????} 5????return?block() 6}

這種語法糖傳遞的參數(shù)就僅僅是一個函數(shù),不是帶接收者對象的函數(shù)字面值,它的返回結(jié)果就是 block 函數(shù)調(diào)用后的結(jié)果。

調(diào)用示例:

1????var?result?=?kotlin.run?{? 2????????????"value" 3????????}

相對于給 arg 變量賦值為 value 字符串。

run 的另一種語法糖:

1public?inline?fun?<T,?R>?T.run(block:?T.()?->?R):?R?{ 2????contract?{ 3????????callsInPlace(block,?InvocationKind.EXACTLY_ONCE) 4????} 5????return?block() 6}

首先,這個語法糖是一個拓展函數(shù),而且用到了泛型?<T,R>,T 類型的拓展函數(shù),返回的是 R 類型,T 和 R 可以相同。

其次,傳遞的參數(shù)是帶接收者對象的函數(shù)字面值,也就是說可以在 block 函數(shù)里面調(diào)用 T 的相關(guān)方法,通過 this 來指代 T ,在 run 方法內(nèi)部就是調(diào)用了 block 方法,返回 block 函數(shù)調(diào)用后的結(jié)果。

調(diào)用示例:

1????????????val?result?=?"a".run?{ 2????????????????this.plus("b") 3????????????}

Contracts DSL

在 run 的語法糖里面還出現(xiàn)了如下一段代碼:

1?contract?{ 2??????callsInPlace(block,?InvocationKind.EXACTLY_ONCE) 3???}

Google 了一番之后

  • https://discuss.kotlinlang.org/t/status-of-kotlin-internal-contracts/6392/2

  • https://stackoverflow.com/questions/49729037/how-does-kotlin-internal-contracts-contractbuilderktcontract-work-in-kotlin

  • https://aisia.moe/2018/03/25/kotlin-contracts-dsl/

  • 得出原來這是 Kotlin 1.2.x 版本中出現(xiàn)的,但實際并沒有用,是 Kotlin 后續(xù)發(fā)展用來解決如下代碼問題的:

    1if?(!x.isNullOrEmpty())?{? 2??//?we?know?that?'x?!=?null'?here? 3??println(x.length) 4}

    假設(shè) x 是可以為 null 的,經(jīng)過 isNullOrEmpty 函數(shù)判斷之后,再執(zhí)行 println 函數(shù),那么它肯定就不是 null 了,就不需要再加兩個?!!?來表示 x 不為 null 了,而現(xiàn)在的情況是要添加?!!?。

    從 Google 來的信息得知, contract 這段代碼就是為了這樣的問題的。

    由于語法糖都有那樣一段代碼,所以就先把它們?nèi)サ袅恕?/p>

    let 語法糖

    1public?inline?fun?<T,?R>?T.let(block:?(T)?->?R):?R?{ 2????return?block(this) 3}

    let 語法糖傳遞的參數(shù)是一個函數(shù),不是帶接收者的函數(shù)字面值,但 block 函數(shù)的參數(shù)就是 T 類型,所以可以在 block 里面調(diào)用 T 類型的方法,但不能通過 this 來指代 T 了,通過 it 來指代 T 類型。

    調(diào)用示例:

    1????????????val?result?=?"a".let?{ 2????????????????it.plus("b") 3????????????}

    with 語法糖

    1public?inline?fun?<T,?R>?with(receiver:?T,?block:?T.()?->?R):?R?{ 2????return?receiver.block() 3}

    with 語法糖不再是一個拓展函數(shù)了,而是需要在語法糖的第一個參數(shù)里面?zhèn)魅虢邮照邔ο蟮膶嵗?#xff0c;第二個參數(shù)就是帶接收者的函數(shù)字面值實例,返回的也是 block 調(diào)用的結(jié)果,這一點和 run 語法糖類似。

    調(diào)用示例:

    1????????????val?result?=?with("a")?{ 2????????????????this.plus("b") 3????????????}

    apply 語法糖

    1public?inline?fun?<T>?T.apply(block:?T.()?->?Unit):?T?{ 2????block() 3????return?this 4}

    apply 語法糖和 run 語法糖都類似,只不過它返回的不是 block 函數(shù)調(diào)用的結(jié)果,而是返回調(diào)用者本身,返回 T 類型。

    also 語法糖

    1public?inline?fun?<T>?T.also(block:?(T)?->?Unit):?T?{ 2????block(this) 3????return?this 4}

    also 語法糖和 let 語法糖有點類似,只不過返回的結(jié)果不是 block 調(diào)用結(jié)果,而是返回它本身,返回 T 類型。

    調(diào)用示例:

    1????????????var?result?=?"a".also?{ 2????????????????it.plus("b") 3????????????}

    takeIf 語法糖

    1public?inline?fun?<T>?T.takeIf(predicate:?(T)?->?Boolean):?T??{ 2????return?if?(predicate(this))?this?else?null 3}

    takeIf 語法糖會調(diào)用 predicate 函數(shù)進(jìn)行判斷,如果為 true 就返回它本身,否則返回 null 。

    takeUnless 語法糖

    1public?inline?fun?<T>?T.takeUnless(predicate:?(T)?->?Boolean):?T??{ 2????return?if?(!predicate(this))?this?else?null 3}

    takeUnless 和 takeIf 正好相反,如果 predicate 返回 false 就返回它本身,否則返回 null 。

    總結(jié)

    這么多的語法糖,其實他們的原理都是類似的,共同點在于都是有返回值的,而區(qū)別就在于對原有的值進(jìn)行了哪些操作,然后如何返回最終的值。

    最后,光是了解他們的原理和調(diào)用情況還是不夠的,再不影響代碼閱讀的情況下要把它們引入到我們的代碼中去,靈活地使用它們。

    總結(jié)

    以上是生活随笔為你收集整理的Kotlin 中的 run、let、with、apply、also、takeIf、takeUnless 语法糖使用和原理分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。