--- title: '智能推斷與 Contact 規則' disqus: kyleAlien --- 智能推斷與 Contact 規則 === ## Overview of Content :::success * 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/) 本篇文章對應的是 [**深入理解 Kotlin:智能推斷與 Contact 規則**](https://devtechascendancy.com/kotlin-smart-inference-contract-rules-guide/) ::: [TOC] ## Kotlin 智能推斷 Koltin 中的智能推斷 **類型** 是一大特色(如同 C++ 中的 auto),可以自動幫我們推斷當前程式的類型 ```kotlin= 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 `!!.` 符號進行修正,才能正常編譯過 ```kotlin= 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` 就不會有問題 ```kotlin= fun printlnLength2(str : String? = null) { if(!str.isNullOrEmpty()) { println(str.length) // Okay } } ``` 這其實是因為該函數內部使用了「`contract`」,以下為 `isNullOrEmpty` 源碼 ```kotlin= @kotlin.internal.InlineOnly public inline fun CharSequence?.isNullOrEmpty(): Boolean { contract { returns(false) implies (this@isNullOrEmpty != null) } return this == null || this.length == 0 } ``` ## Contact 概述 通過契約,開發者可以向編譯器提供使用函數時的相關行為(也可以用來做類似保證的動作),幫助編譯器對程式分析 契約 `Contact` 的實現,就類似於 **開發者和編譯器溝通的橋樑,編譯器必須無條件服從開法者訂下的條件** ### Contract 規則 * Contract 規則 1. **只能使用在頂層 `top-level` 函數,不能用在類中** 2. contract 必須要在函數中的 **首行** 3. **contract 後的所有行為都由開發者負責**,這類似使用 `!!.` 符號 ### Contract 分類 * Contract 分類目前主要有兩種 1. **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= @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 } } ``` 2. **CallInPlace Contracts**:在拓展函數 `let`, `apply` ... 的源碼中會看見;其中的 `callsInPlace` 會通知編譯器,該函數在調用結束後,不會再被執行 | InvocationKind 枚舉 | 說明 | | - | - | | `AT_MOST_ONCE` | 該函數只會被調用一次,或是根本不會調用! | | `EXACTLY_ONCE` | 該函數只會被調用一次 | | `AT_LEAST_ONCE` | 該函數至少被調用一次 | | `UNKNOW` | 該函數可被調用多次 | ```kotlin= @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 語言基礎 :::info * [**Kotlin 函數、類、屬性 | DataClass、Sealed、Object 關鍵字 | Enum、Companion、NPE**](https://devtechascendancy.com/kotlin-functions_oop_dataclass_sealed_object/) * [**深入探究 Kotlin 與 Java 泛型:擦除、取得泛型類型、型變、投影 | 協變、逆變**](https://devtechascendancy.com/explore-kotlin-java-generics_type_erasure/) * [**深入 Kotlin 函數特性:Inline、擴展、標準函數全解析 | 提升程式碼效能與可讀性**](https://devtechascendancy.com/kotlin_inline_extensions_standards-func/) * [**Kotlin DSL、操作符、中綴表達式 Infix | DSL 詳解 | DSL 設計與應用**](https://devtechascendancy.com/kotlin-dsl-operators-infix-explained/) ::: ### Kotlin 特性、特點 * **Kotlin 特性、特點**:探索 Kotlin 的獨特特性和功能,加深對 Kotlin 語言的理解,並增強對於語言特性的應用 :::warning * [**Kotlin 代理與懶加載機制:使用、lazy 深度解析**](https://devtechascendancy.com/kotlin-delegate_java-proxy_lateinit_lazy/) * [**Kotlin Lambda 編程 & Bytecode | Array & Collections 集合 | 集合函數式 API**](https://devtechascendancy.com/kotlin-lambda-bytecode-array-collections-functional/) * [**深入理解 Kotlin:智能推斷與 Contact 規則**](https://devtechascendancy.com/kotlin-smart-inference-contract-rules-guide/) ::: ### Kotlin 進階:協程、響應式、異步 * **Kotlin 進階:協程、響應式、異步**:若想深入學習 Kotlin 的進階主題,包括協程應用、Channel 使用、以及 Flow 的探索,請查看以下文章 :::danger * [**應用 Kotlin 協程:對比 Thread、創建協程、任務掛起 | Dispatcher、CoroutineContext、CoroutineScope**](https://devtechascendancy.com/applied-kotlin-coroutines-in-depth-guide/) * [**Kotlin Channel 使用介紹 | Select、Actor | 生產者消費者**](https://devtechascendancy.com/kotlin-channel_select_actor_cs/) * [**探索 Kotlin Flow:基本使用、RxJava 對比、背壓機制 | Flow 細節**](https://devtechascendancy.com/kotlin-flow-usage_compare-rx_backpressure/) ::: ## Appendix & FAQ :::info ::: ###### tags: `Kotlin`
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up