深入理解 Kotlin:智能推斷與 Contact 規則

深入理解 Kotlin:智能推斷與 Contact 規則

Overview of Content

本文探討 Kotlin 語言中的兩個重要主題:智能推斷和 Contract 規則。

首先,我們探討了 Kotlin 中智能推斷的使用時機,尤其著重於標準庫的應用情境。

接著,我們深入研究 Kotlin Contact 規則的概念和使用方式,涵蓋了不同 Contract 的分類及其實際應用。通過閱讀本文,您將更全面地理解 Kotlin 語言中智能推斷和 Contract 規則的運作原理,並學會如何在實際開發中充分利用這些功能。


Kotlin 智能推斷

Koltin 中的智能推斷 類型 是一大特色(如同 C++ 中的 auto),可以自動幫我們推斷當前程式的類型


abstract class View

class EditView : View() {
    fun input(s: String) {
        println(s)
    }
}

class Button : View() {
    fun click() {
        println("Button be click")
    }
}

fun testView(view: View) {
    when(view) {
        // 不用強制轉型,直接可以使用!(方便 ~
        is EditView -> view.input("TEST")
        is Button -> view.click()
        else -> throw IllegalArgumentException("Error")
    }
}

fun main() {
    testView(EditView())
    
    testView(Button())
}

智能推斷的使用時機:標準庫

● 但 Kotlin 的智能推斷尚未完善,仍可能出現智能判斷失敗,導致編譯不通過的情況,如下狀況

String 已經透過 isNotNull 函數判斷 Not Null,不過下方在使用時仍須透過 ?. or !!. 符號進行修正,才能正常編譯過


fun String?.isNotNull(): Boolean {
    return this != null && this.isNotEmpty()
}

fun printlnLength(str : String? = null) {
    if(str.isNotNull()) {
        
       // 明明上面已經判斷過了
       println(str.length)         // Error

        // println(str?.length)        // Okay
        // println(str!!.length)        // Okay
    }
}

同樣狀況,切換 Kotlin standard lib 中 String 的拓展函數 isNullOrEmpty 就不會有問題


fun printlnLength2(str : String? = null) {
    if(!str.isNullOrEmpty()) {
        println(str.length)         // Okay
    }
}

這其實是因為該函數內部使用了「contract」,以下為 isNullOrEmpty 源碼


@kotlin.internal.InlineOnly
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.length == 0
}

Kotlin Contact 規則、使用

通過契約,開發者可以向編譯器提供使用函數時的相關行為(也可以用來做類似保證的動作),幫助編譯器對程式分析

契約 Contact 的實現,就類似於 開發者和編譯器溝通的橋樑,編譯器必須無條件服從開法者訂下的條件

Contract 規則

● Contract 規則

A. 只能使用在頂層 top-level 函數,不能用在類中

B. contract 必須要在函數中的 首行

C. contract 後的所有行為都由開發者負責,這類似使用 !!. 符號

Contract 分類

● Contract 分類目前主要有兩種

A. Returns Contracts向編譯器保證返回的是某個值,方便編譯器之後判斷是否需要提醒錯誤;一般有下面幾個形式

Returns Contracts說明
returns (true) implies [返回時成立的條件]implies 條件成立時返回 true
returns (false) implies [返回時成立的條件]implies 條件成立時返回 false
returns (null) implies [返回時成立的條件]implies 條件成立時返回 null
returns() implies [返回時成立的條件]implies 條件成立時正常返回
returnsNotNull() implies [返回時成立的條件]implies 條件成立時返回非空數值

@kotlin.internal.InlineOnly
public inline fun <T : Any> requireNotNull(value: T?): T {
    contract {
        // 斷定 value 不為 null 就正常返回
        returns() implies (value != null)
    }
    // 走到這一步,就確定  value 為 null
    
    // 查看 requireNotNull 函數
    return requireNotNull(value) { "Required value was null." }
}

public inline fun <T : Any> requireNotNull(value: T?, lazyMessage: () -> Any): T {
    contract {
        // 斷定 value 不為 null 就正常返回
        returns() implies (value != null)
    }
    // 走到這一步,就確定  value 為 null

    if (value == null) {
        val message = lazyMessage()
        throw IllegalArgumentException(message.toString())
    } else {
        return value
    }
}

B. CallInPlace Contracts:在拓展函數 let, apply ... 的源碼中會看見;其中的 callsInPlace 會通知編譯器,該函數在調用結束後,不會再被執行

InvocationKind 枚舉說明
AT_MOST_ONCE該函數只會被調用一次,或是根本不會調用!
EXACTLY_ONCE該函數只會被調用一次
AT_LEAST_ONCE該函數至少被調用一次
UNKNOW該函數可被調用多次

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    
    // 走到這裡就保證,Lambda 函數只會被呼叫一次
    block()
    return this
}

更多的 Kotlin 語言相關文章

在這裡,我們提供了一系列豐富且深入的 Kotlin 語言相關文章,涵蓋了從基礎到進階的各個方面。讓我們一起來探索這些精彩內容!

Kotlin 特性、特點

Kotlin 特性、特點:探索 Kotlin 的獨特特性和功能,加深對 Kotlin 語言的理解,並增強對於語言特性的應用

Kotlin 進階:協程、響應式、異步

Kotlin 進階:協程、響應式、異步:若想深入學習 Kotlin 的進階主題,包括協程應用、Channel 使用、以及 Flow 的探索,請查看以下文章

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

發表迴響