---
# System prepended metadata

title: 智能推斷與 Contact 規則
tags: [Kotlin]

---

---
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
        }
    }
    ```
    > ![](https://i.imgur.com/8kXDbXu.png)

    同樣狀況，切換 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`
