亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Kotlin中的contract到底有什么用詳解

 更新時間:2022年01月20日 09:15:23   作者:讓開,我要吃人了  
Kotlin contracts是一種通知編譯器有關(guān)函數(shù)行為的方式,下面這篇文章主要給大家介紹了關(guān)于Kotlin中contract到底有什么用的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

我們在開發(fā)中肯定會經(jīng)常用Kotlin提供的一些通用拓展函數(shù),當(dāng)我們進(jìn)去看源碼的時候會發(fā)現(xiàn)許多函數(shù)里面有contract {}包裹的代碼塊,那么這些代碼塊到底有什么作用呢??

測試

接下來用以下兩個我們常用的拓展函數(shù)作為例子

public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
 
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }
 
    return this == null || this.length == 0
}

run和isNullOrEmpty我相信大家在開發(fā)中是經(jīng)常見到的。

不知道那些代碼有什么作用,那么我們就把那幾行代碼去掉,然后看看函數(shù)使用起來有什么區(qū)別。

public inline fun <T, R> T.runWithoutContract(block: T.() -> R): R {
    return block()
}
 
public inline fun CharSequence?.isNullOrEmptyWithoutContract(): Boolean {
    return this == null || this.length == 0
}

上面是去掉了contract{}代碼塊后的兩個函數(shù) 調(diào)用看看

fun test() {
    var str1: String = ""
    var str2: String = ""
 
    runWithoutContract {
        str1 = "jayce"
    }
    run {
        str2 = "jayce"
    }
 
    println(str1) //jayce
    println(str2) //jayce
}

經(jīng)過測試發(fā)現(xiàn),看起來好像沒什么問題,run代碼塊都能都正常執(zhí)行,做了賦值的操作。

那么如果是這樣呢

將str的初始值去掉,在run代碼塊里面進(jìn)行初始化操作

@Test
fun test() {
    var str1: String
    var str2: String 
 
    runWithoutContract {
        str1 = "jayce"
    }
    run {
        str2 = "jayce"
    }
 
    println(str1) //編譯不通過 (Variable 'str1' must be initialized)
    println(str2) //編譯通過
}

??????

我們不是在runWithoutContract做了初始化賦值的操作了嗎?怎么IDE還報錯,難道是IDE出了什么問題?好 有問題就重啟,我去,重啟還沒解決。。。。好重裝。不不不??!別急 會不會Contract代碼塊就是干這個用的?是不是它悄悄的跟IDE說了什么話 以至于它能正常編譯通過?

好 這個問題先放一放 我們再看看沒contract版本的isNullOrEmpty對比有contract的有什么區(qū)別

fun test() {
    val str: String? = "jayce"
 
    if (!str.isNullOrEmpty()) {
        println(str) //jayce
    }
    if (!str.isNullOrEmptyWithoutContract()) {
        println(str) //jayce
    }
}

發(fā)現(xiàn)好像還是沒什么問題。相信大家根據(jù)上面遇到的問題可以猜測,這其中肯定也有坑。

比如這種情況

fun test() {
    val str: String? = "jayce"
 
    if (!str.isNullOrEmpty()) {
        println(str.length) // 編譯通過
    }
 
    if (!str.isNullOrEmptyWithoutContract()) {
        println(str.length) // 編譯不通過(Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?)
    }
}

根據(jù)錯誤提示可以看出,在isNullOrEmptyWithoutContract判斷為flase之后的代碼塊,str這個字段還是被IDE認(rèn)為是一個可空類型,必須要進(jìn)行空檢查才能通過。然而在isNullOrEmpty返回flase之后的代碼塊,IDE認(rèn)為str其實已經(jīng)是非空了,所以使用前就不需要進(jìn)行空檢查。

查看 contract 函數(shù)

public inline fun contract(builder: ContractBuilder.() -> Unit) { }

點(diǎn)進(jìn)去源碼,我們可以看到contract是一個內(nèi)聯(lián)函數(shù),接收一個函數(shù)類型的參數(shù),該函數(shù)是ContractBuilder的一個拓展函數(shù)(也就是說在這個函數(shù)體里面擁有ContractBuilder的上下文)

看看ContractBuilder給我們提供了哪些函數(shù)(主要就是依靠這些函數(shù)來約定我們自己寫的lambda函數(shù))

public interface ContractBuilder {
      //描述函數(shù)正常返回,沒有拋出任何異常的情況。
    @ContractsDsl public fun returns(): Returns
 
      //描述函數(shù)以value返回的情況,value可以取值為 true|false|null。
    @ContractsDsl public fun returns(value: Any?): Returns
  
      //描述函數(shù)以非null值返回的情況。
    @ContractsDsl public fun returnsNotNull(): ReturnsNotNull
 
       //描述lambda會在該函數(shù)調(diào)用的次數(shù),次數(shù)用kind指定
    @ContractsDsl public fun <R> callsInPlace(lambda: Function<R>, kind: InvocationKind = InvocationKind.UNKNOWN): CallsInPlace
}

returns

其中 returns() returns(value) returnsNotNull() 都會返回一個繼承于SimpleEffect的Returns 接下來看看SimpleEffect

public interface SimpleEffect : Effect {
      //接收一個Boolean值的表達(dá)式 改函數(shù)用來表示當(dāng)SimpleEffect成立之后 保證Boolean值的表達(dá)式返回值為true
      //表達(dá)式可以傳判空代碼塊(`== null`, `!= null`)判斷實例語句 (`is`, `!is`)。
    public infix fun implies(booleanExpression: Boolean): ConditionalEffect
}

可以看到SimpleEffect里面有一個中綴函數(shù)implies ??梢允褂肅ontractBuilder的函數(shù)指定某種返回的情況 然后用implies來聲明傳入的表達(dá)式為true。

看到這里 那么我們應(yīng)該就知道 isNullOrEmpty() 加的contract是什么意思了

public inline fun CharSequence?.isNullOrEmpty(): Boolean {
    contract {
          //返回值為false的情況 returns(false)
                //意味著 implies
                //調(diào)用該函數(shù)的對象不為空 (this@isNullOrEmpty != null)
        returns(false) implies (this@isNullOrEmpty != null)
    }
  
    return this == null || this.length == 0
}

因為isNullOrEmpty里面加了contract代碼塊,告訴IDE說:返回值為false的情況意味著調(diào)用該函數(shù)的對象不為空。所以我們就可以直接在判斷語句后直接使用非空的對象了。

有些同學(xué)可能還是不理解,這里再舉一個沒什么用的例子(運(yùn)行肯定會crash哈。。。)

@ExperimentalContracts //因為該特性還在試驗當(dāng)中 所以需要加上這個注解
fun CharSequence?.isNotNull(): Boolean {
    contract {
          //返回值為true returns(true)
          //意味著implies
          //調(diào)用該函數(shù)的對象是StringBuilder (this@isNotNull is StringBuilder)
        returns(true) implies (this@isNotNull is StringBuilder)
    }
 
    return this != null
}
 
fun test() {                                                                                               
    val str: String? = "jayce"                                                                             
                                                                                                           
    if (str.isNotNull()) {                                                                                 
        str.append("")//String可是沒有這個函數(shù)的,因為我們用contract讓他強(qiáng)制轉(zhuǎn)換成StringBuilder了 所以才有了這個函數(shù)                       
    }                                                                                                      
}

是的 這樣IDE居然沒有報錯,因為經(jīng)過我們contract的聲明,只要這個函數(shù)返回true,調(diào)用函數(shù)的對象就是一個StringBuilder。

callsInPlace

//描述lambda會在該函數(shù)調(diào)用的次數(shù),次數(shù)用kind指定
@ContractsDsl public fun <R> callsInPlace(lambda: Function<R>, kind: InvocationKind = InvocationKind.UNKNOWN): CallsInPlace

可以知道callsInPlace是用來指定lambda函數(shù)調(diào)用次數(shù)的

kind有四種取值

  • InvocationKind.AT_MOST_ONCE:最多調(diào)用一次
  • InvocationKind.AT_LEAST_ONCE:最少調(diào)用一次
  • InvocationKind.EXACTLY_ONCE:調(diào)用一次
  • InvocationKind.UNKNOWN:未知,不指定的默認(rèn)值

我們再看回去之前run函數(shù)里面的contract聲明了什么

public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
          //block這個函數(shù),剛好調(diào)用一次
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

看到這里 應(yīng)該就知道為什么我們自己寫的runWithoutContract會報錯(Variable 'str1' must be initialized),而系統(tǒng)的run卻不會報錯了,因為run聲明了lambda會調(diào)用一次,所以就一定會對str2做初始化操作,然而runWithoutContract卻沒有聲明,所以IDE就會報錯(因為有可能不會調(diào)用,所以就不會做初始化操作了)。

總結(jié)

Kotlin提供了一些自動轉(zhuǎn)換的功能,例如平時判空和判斷是否為某個實例的時候,Kotlin都會為我們自動轉(zhuǎn)換。但是如果這個判斷被提取到其他函數(shù)的時候,這個轉(zhuǎn)換會失效。所以提供了contract給我們在函數(shù)體添加聲明,編譯器會遵守我們的約定。

當(dāng)使用一個高階函數(shù)的時候,可以使用callsInPlace指定該函數(shù)會被調(diào)用的次。例如在函數(shù)體里面做初始化,如果申明為EXACTLY_ONCE的時候,IDE就不會報錯,因為編譯器會遵守我們的約定。

到此這篇關(guān)于Kotlin中的contract到底有什么用的文章就介紹到這了,更多相關(guān)Kotlin contract用處內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 簡單談?wù)勎业腁ndroid屏幕適配之路

    簡單談?wù)勎业腁ndroid屏幕適配之路

    我相信Android碎片化問題是讓所有的Android開發(fā)者都比較頭疼的問題.尤其是屏幕適配這一塊兒.想要自己的app在不同的設(shè)備上面都有一個比較好的顯示效果.就必須做好相應(yīng)的屏幕適配.
    2017-11-11
  • android通過bitmap生成新圖片關(guān)鍵性代碼

    android通過bitmap生成新圖片關(guān)鍵性代碼

    android通過bitmap生成新圖片具體實現(xiàn)如下,感興趣的朋友可以參考下哈,希望對你有所幫助
    2013-06-06
  • Android圖片無限輪播的實現(xiàn)代碼

    Android圖片無限輪播的實現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了Android圖片無限輪播的實現(xiàn)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Flutter投票組件使用方法詳解

    Flutter投票組件使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了Flutter投票組件的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Android實現(xiàn)動態(tài)向Gallery中添加圖片及倒影與3D效果示例

    Android實現(xiàn)動態(tài)向Gallery中添加圖片及倒影與3D效果示例

    這篇文章主要介紹了Android實現(xiàn)動態(tài)向Gallery中添加圖片及倒影與3D效果的方法,涉及Android針對圖片的加載、顯示、翻轉(zhuǎn)、倒影等相關(guān)特效功能實現(xiàn)技巧
    2016-08-08
  • Android實現(xiàn)靜默拍照功能

    Android實現(xiàn)靜默拍照功能

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)靜默拍照,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • Android實現(xiàn)簡單下拉篩選框

    Android實現(xiàn)簡單下拉篩選框

    這篇文章主要為大家詳細(xì)介紹了一款簡單靈活的Android下拉篩選框,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • Android添加glide庫報錯Error: Failed to resolve: com.android.support:support-annotations:26.0.2的解決

    Android添加glide庫報錯Error: Failed to resolve: com.android.suppo

    這篇文章主要給大家介紹了關(guān)于Android添加glide庫報錯Error: Failed to resolve: com.android.support:support-annotations:26.0.2的解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-11-11
  • Android編程實現(xiàn)抽屜效果的方法示例

    Android編程實現(xiàn)抽屜效果的方法示例

    這篇文章主要介紹了Android編程實現(xiàn)抽屜效果的方法,結(jié)合具體實例形式分析了Android抽屜效果的布局、功能實現(xiàn)及相關(guān)注意事項,需要的朋友可以參考下
    2017-06-06
  • Android 模擬器(JAVA)與C++ socket 通訊 分享

    Android 模擬器(JAVA)與C++ socket 通訊 分享

    Android 模擬器(JAVA)與C++ socket 通訊 分享,需要的朋友可以參考一下
    2013-05-05

最新評論