91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Kotlin中的contract怎么用

發布時間:2022-01-20 11:01:52 來源:億速云 閱讀:141 作者:小新 欄目:開發技術

這篇文章將為大家詳細講解有關Kotlin中的contract怎么用,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

測試

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

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我相信大家在開發中是經常見到的。

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

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{}代碼塊后的兩個函數 調用看看

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

經過測試發現,看起來好像沒什么問題,run代碼塊都能都正常執行,做了賦值的操作。

那么如果是這樣呢

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

@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的有什么區別

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

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

比如這種情況

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?)
    }
}

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

查看 contract 函數

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

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

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

public interface ContractBuilder {
      //描述函數正常返回,沒有拋出任何異常的情況。
    @ContractsDsl public fun returns(): Returns
 
      //描述函數以value返回的情況,value可以取值為 true|false|null。
    @ContractsDsl public fun returns(value: Any?): Returns
  
      //描述函數以非null值返回的情況。
    @ContractsDsl public fun returnsNotNull(): ReturnsNotNull
 
       //描述lambda會在該函數調用的次數,次數用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值的表達式 改函數用來表示當SimpleEffect成立之后 保證Boolean值的表達式返回值為true
      //表達式可以傳判空代碼塊(`== null`, `!= null`)判斷實例語句 (`is`, `!is`)。
    public infix fun implies(booleanExpression: Boolean): ConditionalEffect
}

可以看到SimpleEffect里面有一個中綴函數implies 。可以使用ContractBuilder的函數指定某種返回的情況 然后用implies來聲明傳入的表達式為true。

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

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

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

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

@ExperimentalContracts //因為該特性還在試驗當中 所以需要加上這個注解
fun CharSequence?.isNotNull(): Boolean {
    contract {
          //返回值為true returns(true)
          //意味著implies
          //調用該函數的對象是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可是沒有這個函數的,因為我們用contract讓他強制轉換成StringBuilder了 所以才有了這個函數                       
    }                                                                                                      
}

是的 這樣IDE居然沒有報錯,因為經過我們contract的聲明,只要這個函數返回true,調用函數的對象就是一個StringBuilder。

callsInPlace

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

可以知道callsInPlace是用來指定lambda函數調用次數的

kind有四種取值

  • InvocationKind.AT_MOST_ONCE:最多調用一次

  • InvocationKind.AT_LEAST_ONCE:最少調用一次

  • InvocationKind.EXACTLY_ONCE:調用一次

  • InvocationKind.UNKNOWN:未知,不指定的默認值

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

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

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

關于“Kotlin中的contract怎么用”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

彭泽县| 碌曲县| 仲巴县| 庆阳市| 闽侯县| 南华县| 龙州县| 青冈县| 翁牛特旗| 高碑店市| 浦东新区| 多伦县| 德格县| 余姚市| 延长县| 浦江县| 西昌市| 宁城县| 肥城市| 湘潭市| 吉林市| 浏阳市| 沁阳市| 临夏县| 汤原县| 磴口县| 长丰县| 巴彦淖尔市| 尼木县| 普定县| 确山县| 庐江县| 昌黎县| 南溪县| 丹巴县| 肥乡县| 桂阳县| 轮台县| 岳阳市| 社会| 安阳市|