---
# System prepended metadata

title: Inline 內聯 & 拓展函數 & Kotlin 標準函數
tags: [Kotlin]

---

---
title: 'Inline 內聯 & 拓展函數 & Kotlin 標準函數'
disqus: kyleAlien
---

Inline 內聯 & 拓展函數 & Kotlin 標準函數
===

## Overview of Content

以下參考，第一行代碼 (第三版), Kotlin 進階實戰

如有引用參考本文章請詳註出處，感謝 :smile:

:::success
* 如果喜歡讀更好看一點的網頁版本，可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
    
    本篇文章對應的是 [**深入 Kotlin 函數特性：Inline、擴展、標準函數全解析 | 提升程式碼效能與可讀性**](https://devtechascendancy.com/kotlin_inline_extensions_standards-func/)
:::
[TOC]

## Kotlin Inline 內聯函數

內聯（`inline`）函數主要是 **透過編譯器來將我們的程式在編譯階段直接嵌入程式中**，減少呼叫函數時 Stack 出入棧的消耗，**也就是 空間換時間** (**但也不要亂用**)

:::warning
* **`inline` 使用注意**

    在使用 inline 函數時，如果用在「遞迴」則有一些編譯器可能進入無窮編譯的情況
:::

### Inline 基礎使用

* 這邊要配合 Kotlin Bytecode 工具一起看，接下來我們會觀察 `.class` 編譯出的程式，比對普通函數與 `inline` 在字節碼的差異

    ```kotlin=
    fun nonInlineFunc(block : () -> Unit) {
        block()
    }

    inline fun inlineFunc(block : () -> Unit) {
        block()
    }

    fun main() {
        nonInlineFunc {
            println("No Inline function")
        }

        inlineFunc {
            println("Inline function")
        }
    }
    ```
    1. **`nonInlineFunc` 函數**：必須要調用 Function 對象來完成 lambda 呼叫（使用了 `INVOKESTATIC` 呼叫函數）

        > ![](https://i.imgur.com/GspOwxj.png)

        呼叫 Function0#invoke 函數後就會有出入棧的消耗
        > ![](https://i.imgur.com/viBfivL.png)

        
    2. **`inlineFunc` 函數**：可以看到呼叫 **inline 函數不需要出入棧就可以呼叫**（它被直接編入呼叫的函數內），所以可以節省部分資源

        > ![](https://i.imgur.com/aQ5RCWL.png)

### 禁用內聯：noinline

* 當我們想要拒絕編譯器將程式當作內聯編入時，可以使用 `noinline` 關鍵字，像是我們可以使用在…

    接收參數時可以使用 `noinline` 關鍵字來限制編譯器不要使用內聯函數的功能

    ```kotlin=
    fun func_1() {
        println("Do something with inline.")
    }

    fun func_2() {
        println("Do something with inline.")
    }

    inline fun callFunction(fun1: () -> Unit,
                            // 使用 noinline 關鍵字
                            noinline fun2: () -> Unit) {
        fun1()
        fun2()
    }


    fun main() {
        callFunction(::func_1, ::func_2)
    }
    ```
    1. 從 `.class` 檔可以看出端倪，呼叫時需要使用到 Function 關鍵字
    
        > ![](https://i.imgur.com/3DDIpOz.png)
    
    2. 反組譯過後的更清楚可以看到 `noninline` 標示的參數會使用 FunctionN 來呼叫 invoke

        > ![](https://i.imgur.com/iZziyE0.png)


### Non-local return： Inline make Lambda can return

* **Kotlin 在 lambda 函數中返回是無法跳出函數**

    ```kotlin=
    fun normalFunc(block: () -> Unit) {
        block()
    }

    fun foo() {
        println("Start")

        normalFunc {
            return        // Error: Not allow here
        }

        println("Done")
    }
    ```
    > ![](https://i.imgur.com/ArxIUdx.png)

    1. 可以使用 `@tag` 返回到指定地點

        ```kotlin=
        fun normalFunc(block: () -> Unit) {
            block()
        }
        
        fun foo() {
            println("Start")

            normalFunc {
                return@normalFunc    // 修正
            }

            println("Done")    // 會執行到
        }
        ```

    2. 如果是 **`inline` 函數則可以用 return**

        ```kotlin=
        inline fun inlineFunction(block: () -> Unit) {
            block()
        }

        fun foo2() {
            println("Start")

            inlineFunction {        // 修正
                // return okay
                return
            }

            println("Done")    // 不會執行到 Done
        }
        ```

### 禁止 inline 返回：crossinline

* **`crossinline` 關鍵字用來修飾入參**（函數的參數），一般來說 inline 函數可以局部返回，如果這時 **使用了 `crossinline` 那就禁止局部返回**

    :::info
    `crossinline` 必須配合 `inline` 一起使用
    :::

    ```kotlin=
    inline fun startFunc(crossinline lambda: () -> Unit) {
        println("Start")

        lambda()

        println("Done")
    }

    fun main() {
        startFunc {
            return          // Error!  Not allow here
        }
    }
    ```

    > ![](https://i.imgur.com/Qd3TRBv.png)

## Kotlin 拓展函數

Kotlin 拓展函數是與 Java 很不同的特點之一，拓展函數讓 Kotlin 更接近現在語言，如果配合 Lambda 可以做到更多的事情…

> 拓展函數在 `C#`, `Swift` 都有實現這種語言特性

Kotlin 拓展函數的格式如下

```shell=
<原始類>.<拓展名>() {
    // TODO:
}
```


### 擴展函數使用

* 依照拓展函數的格式寫，就可以建構拓展函數

    ```kotlin=
    // 原始類
    class BaseExtension {
        fun hello() = println("Hello")
    }

    // 拓展函數
    fun BaseExtension.world() = println("Extension World")

    fun main() {
        val extension = BaseExtension()

        extension.hello()
        extension.world()
    }
    ```
    > ![](https://i.imgur.com/r1TOEAA.png)

    :::info
    * 使用 Kotlin Bytecode 工具去反編譯，可以發現 **拓展函數就是一個靜態工具類**！並不會影響到原始類

        > ![](https://i.imgur.com/j7CyBbF.png)
    :::

### 拓展函數：注意事項

1. 拓展類如果跟原始類有相同名稱（包括參數）會使用哪個呢？ 呼叫時會 **使用原始類**（原始類的優先權更高）

    ```kotlin=
    class BaseExtension {
        fun hello() = println("Hello")
    }

    // 拓展類（與原始相同函數名）
    fun BaseExtension.hello() = println("Extension Hello")
    fun BaseExtension.world() = println("Extension World")

    fun main() {
        val extension = BaseExtension()

        extension.hello()
        extension.world()
    }
    ```

    :::warning
    * 可以看到反組繹出來後，它呼叫的不是靜態的 `hello` 函數，而是原始類的 `hello` 函數
        
        > ![](https://i.imgur.com/NgLEboa.png)
    :::
    
2. 拓展類沒有多態（繼承重寫沒有用）

    ```kotlin=
    open class BaseExtension {
        fun hello() = println("Hello")
    }


    fun BaseExtension.world() = println("Extension World (BaseExtension)")

    class NewExtension : BaseExtension()

    // 拓展相同名稱的函數
    fun NewExtension.world() = println("Extension World (NewExtension)")

    fun showWorld(ex : BaseExtension) {
        ex.world()
    }

    fun main() {

        val baseEx = BaseExtension()
        val newEx = NewExtension()

        showWorld(baseEx)
        showWorld(newEx)
    }
    ```
    > ![](https://i.imgur.com/mi1zR3c.png)

    :::warning
    * 原因是**編譯器會強制轉型為基類**（因為 `showWorld` 函數接收的就是基類），並呼叫期靜態，而不是透過對象呼叫

        > ![](https://i.imgur.com/VKa9iR8.png)

        > ![](https://i.imgur.com/cLhb3Ll.png)
    :::

3. **Java 類也可以直接呼叫拓展函數**：目標類名就是「**檔案名稱 + Kt**」，之後再加上要呼叫的函數名即可，範例如下

    ```java=
    public class Java_Main {
        public static void main(String[] args) {
            BaseExtensionKt.world(new BaseExtension());
        }
    }
    ```

## Kotlin 拓展屬性

Kotlin 除了可以拓展函數以外，還可以拓展屬性（`property`）

### 拓展屬性使用


```kotlin=
class ExtensionField

val ExtensionField.name : String
    get() = "Alien"

fun main() {
    val ex = ExtensionField()
    println(ex.name)
}
```

:::danger
* 拓展屬性 **沒有 `backing field` 可以使用**！所以 **不能設定 setter**，所以只能用 val 描述！

* 如果還是想用 Setter，那可以考慮這樣設計：如果你是拓展某些類型，並且該類型內「**可以儲存你自訂設定的參數**」，就可以使用 setter；
    
    範例如下：將自己要設定的物件存入 Throwable 中的 `List<Throwable>` 列表內，並透過該 list 遍歷並判斷，取得對應的物件

    ```kotlin=
    interface IExceptionInfo {

        val isHandleable : Boolean

        fun timeOccurred() : Long
    }

    const val SET_FIELD_FAIL = "Cannot add not Throwable type."

    // 拓展所有 Throwable（或其子類）的屬性（添加 `errorDetails` 屬性）
    var <E: Throwable> E.errorDetails: IExceptionInfo

        get() {
            // Throwable#suppressedExceptions 列表中取出相對數據
            suppressedExceptions.forEach { e ->
                // 針對類型判斷
                if (e is IExceptionInfo) {
                    return e
                }
            }
            return Throwable().unHandleable {  }.errorDetails
        }

        set(value) {
            if (value is Throwable) {
                val throwable = value as Throwable

                // 數據存入 Throwable#suppressedExceptions 列表
                if (!suppressedExceptions.contains(throwable)) {
                    // 間接使用的原先類的內部成員，來保存自己需要的數據
                    addSuppressed(throwable)
                }
                return
            }

            throw Exception(SET_FIELD_FAIL)
        }
    ```
    
:::



## Kotlin 標準函數 - Standard.kt
**Kotlin 的標準函數指的是 ++Standaed.kt (`kotlin-stdlib.jar`)++ 文件中定意義的函數**，任何 Kotlin 代碼都可以自由的調用標準函數

:::success
* 以下這些標準函數使用的好，可以增強程式的可讀性！
:::

### let 函數
* 每個對象內都有一個 `let` 工具 (就類似於這個 `let` 在 Object 內)，它並不是關鍵字、操作符號，它就是一個函數，**let 函數會把自身對象再次傳入，作為參數再次使用**

    ```kotlin=
    // let 原型

    @kotlin.internal.InlineOnly
    public inline fun <T, R> T.let(block: (T) -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return block(this)
    }
    ```

    **let 常用來判空**，可以用在判斷對象不為空後，繼續往下操作
    ```kotlin=
    fun useLet(s: BookShop?) {
        // 可以統一盼空
        // 原型 : public inline fun <T, R> T.let(block: (T) -> R): R { }
        s?.let { shop ->
            shop.getDescribe()
            shop.workTime()
        }

        // 單一參數所以可以使用 it 取代
        s?.let {
            it.getDescribe()
            it.workTime()
        }
    }
    ```
    
    :::danger
    * **可能會想 `if` 統一判斷與 `let` 是相同的，但是並非如此 !**

        若是**把變數放置全域 `var` 則 if 也無法正確判斷**
        
        假設在 **多執行緒（線程）協作的狀況下**，有可能改動到全域變數，也就是 `if` 也無法保證該變量的安全性 (以往我們會使用鎖來解決這個問題)，**而使用 `let` 則可以安全的操作**

        ```kotlin=
        var s : BookShop? = null

        fun main() {
            /**
             * Smart cast to 'BookShop' is impossible, 
             * because 's' is a mutable property that could have been changed by this time
             */
        //    if(s != null) {   
        //        s.getDescribe()
        //        s.workTime()
        //    }
            s?.let {
                it.getDescribe()
                it.workTime()
            }
        }
        ```

        > ![](https://i.imgur.com/qaXZH9x.png)
    :::


### run 函數
* `run` 功能與 `with` 功能相似，**每個對象裡面都有 run 方法可以調用，而它的上下文就是調用的 `run` 的對象**，下面會使用 run 達到相同目的

    ```kotlin=
    // run 原型
    @kotlin.internal.InlineOnly
    public inline fun <T, R> T.run(block: T.() -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return block()
    }
    ```
    可以看到 run 是必須由對象 T (泛型)來調用的

    ```kotlin=
    fun main() {

        val myList = listOf<String>("Apple", "Banana", "Orange","Pear", "Grape")
        val strBuild = StringBuilder()
        val str = strBuild.run {
            append("Start Search...\n")
            for (i in myList) {
                append(i).append("\n")
            }
            append("Finish")
            toString()  //  1. return String object
        }
        println("str type -> ${str.javaClass}")
        println("StringBuilder run function: $str")
    }
    ```
    > 這邊最後使用了 toString 所以就會返回 String 對象

    **--實作結果--**
    > ![](https://i.imgur.com/wPHbgF9.png)

    :::success
    * **run 可以解決鏈式呼叫** !

        ```kotlin=
        fun createHelloObj() = Hello()


        class Hello {
            fun getWorldObj() = World()
        }

        class World {
            fun getMsg() = "Hello World, run~"
        }]
        ```
        
        1. 傳統 **鏈式呼叫**
        
            ```kotlin=
            // 鏈式呼叫

            println(createHelloObj().getWorldObj().getMsg())
            ```

        2. **使用 run 函數**：更清晰的可看出調用順序
        
            ```kotlin=
            // 使用 run 函數 

            createHelloObj()
                .run {
                    getWorldObj()
                }.run {
                    getMsg()
                }.run (::println)
            ```
            > ![](https://i.imgur.com/sDRiO9c.png)

    :::
    
    :::info
    * **`run` & `apply` 差別**
        
        1. **參數**：run & apply 兩者都沒有參數，lambda 內部都作用於接收者
        
        2. **返回**：**`run` 不返回接收者**，它可返回其他對象 (如同 `let`)；`apply` 則反為接收者，無法返回其他類型！
    :::

### apply 函數
* `apply` 與 `run` 相似，與 run 的差異處在，**apply 只會返回該對象本身，++run 可以返回呼叫對象以外的對象++**

    ```kotlin=
    // apply 原型
    @kotlin.internal.InlineOnly
    public inline fun <T> T.apply(block: T.() -> Unit): T {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        block()
        return this
    }
    ```
    可以看到 apply 只有一個泛型，傳遞進該類後，就只會返回該類

    ```kotlin=
    fun main() {

        val myList = listOf<String>("Apple", "Banana", "Orange","Pear", "Grape")
        val strBuild = StringBuilder()
        val str = strBuild.apply {
            append("Start Search...\n")
            for (i in myList) {
                append(i).append("\n")
            }
            append("Finish")
            toString()  //     no Error, but still is StringBuilder type
        }
        println("str type -> ${str.javaClass}")
        println("StringBuilder run function: $str")
    }
    ```
    **--實作結果--**
    > ![](https://i.imgur.com/4nUGXB2.png)

    :::info
    * **`apply` & `let` 差別**

        1. **參數**：apply 沒有接收參數；let 接收原本接收者的參數

        2. **返回**：apply 返回原接收者；let 可返回不同的對象 (最後一行)
    :::



### with 函數

* **`with` 函數與 `run` 函數類似，是 `run` 函數的變形**；with 接收兩個參數，^1.^ 任意對象、^2.^ Lambda (閉包)函數


* **with 會提供第一個參數的上下文，讓 Lambda 內可以自由使用，並使用最後一行為返回值**，它可以在連續調用程式時看起來更簡潔

    ```kotlin=
    // with 原型
    @kotlin.internal.InlineOnly
    public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return receiver.block()
    }
    ```

    以下遍歷一個 list，並使用 StringBuilder 串接字串
    ```kotlin=
    import java.lang.StringBuilder

    fun main() {

        val list = listOf<String>("Apple", "Banana", "Orange","Pear", "Grape")

        // 一般寫法
        val str = StringBuilder()   // 省略 new
        str.append("Start Search...\n")
        for ( i in list) {
            str.append(i).append(" ")
        }
        str.append("\nFinish")
        var r = str.toString()
        println("Older Style: $r \n")


        // 1. Kt Standard 寫法 
        val result = with(StringBuilder()) {
            this.append("Start Search...\n")    // 遞一個參數為上下文 this
            for(i in list) {
                append(i).append(" ")
            }
            append("\nFinish")    // 2. 返回
        }
        r = result.toString()
        println("new Style: $r \n")
    }
    ```
    1. 可以看出使用標準 with 函數讓程式更加精簡，它讓第一個參數傳入 StringBuilder 對象，第二個參數內的上下文就是 StringBuilder（this），就可以省略上下文呼叫

    2. 最後一行最為返回值返回，而 append 函數返回的也是該對象
        
        ```java=
        // append 函數
        public StringBuilder append(String var1) {
            super.append(var1);

            return this;    // 返回自身對象
        }
        ```
        **--實作結果--**
        > ![](https://i.imgur.com/BkzVWLS.png)

### repeat 函數
* repeat 接收一個常數 n，然後會把 Lambda 中的表達式運行你指定的次數 (前面設定的常數 n)

    ```kotlin=
    // kotlin 原型
    @kotlin.internal.InlineOnly
    public inline fun repeat(times: Int, action: (Int) -> Unit) {
        contract { callsInPlace(action) }

        for (index in 0 until times) {
            action(index)
        }
    }
    ```
    repeat 函數使用範例如下：以下用來串接字符串
    ```kotlin=
    // 使用範例
    import java.lang.StringBuilder

    fun main() {
        val str = StringBuilder()

        repeat(5) {
            str.append("Hello $it\n")   // 從 0 開始
        }

        println("use repeat function: \n${str.toString()}")
    }
    ```
    **--實作結果--**
    > ![](https://i.imgur.com/2KYyM4P.png)

### also 函數

* also 是 kotlin 新增的函數（其語意可以理解為「順帶做某些事情」），also 原型如下

    ```kotlin=
    @kotlin.internal.InlineOnly
    @SinceKotlin("1.1")
    public inline fun <T> T.also(block: (T) -> Unit): T {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        block(this)
        return this
    }
    ```

* also 與 apply 類似，不過**內部可以透過 it 來指定自身對象 (也就是 also 有接收者參數)**，並且返回的也是自身對象

    ```kotlin=
    fun main() {
        val res = "Hello".also {
            println("$it World")

            "$it World"    // 返回仍然是 Hello
        }

        println(res)
    }
    ```
    > ![](https://i.imgur.com/pqdrwuG.png)

    :::info
    * also 適合針對同一原始物件，**透過副作用做事**

        ```kotlin=
        fun main() {
            val strList = mutableListOf<String>()

            "Hello".also {
                // Hello 的副作用
                println("\"$it\"  add to list")
            }.also {
                // Hello 的副作用
                strList.add(it)
            }

            println(strList)
        }
        ```
        > ![](https://i.imgur.com/3QGdvOJ.png)

    :::
    
### takeIf / takeUnless 函數

* takeIf / takeUnless 函數 其實就是一個 If 判斷式的方便寫法，**再執行 predicate 判斷回復為 true 則返回接收者，false 則返回 null**；它可以讓我們省略不必要的臨時變量

    1. **`takeIf` 原碼**

        ```kotlin=
        @kotlin.internal.InlineOnly
        @SinceKotlin("1.1")
        public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
            contract {
                callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
            }
            return if (predicate(this)) this else null
        }
        ```
        
        :::info
        * 使用方式就像是 `if(...)` 的判斷
        
            ```kotlin=
            fun useIfElse(msg: String) : String? {
                // 省略臨時變量
                val predicate = msg.length > 5

                return if (predicate) {
                    msg
                } else
                    null
            }

            ```
        :::
        
        `takeIf` 使用方式如下
        
        ```kotlin=
        fun useTaskIf(msg: String) : String? {
            // 不符合條件則返回 null

            return msg.takeIf {
                it.length > 5
            }?.toString()

        }


        fun main() {
            useTaskIf("Hello World").also {
                println(it)
            }

            useTaskIf("Hello").also {
                println(it)
            }
        }
        ```
        > ![](https://i.imgur.com/W1XJwAY.png)


    2. **`takeUnless` 原碼**

        ```kotlin=
        @kotlin.internal.InlineOnly
        @SinceKotlin("1.1")
        public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
            contract {
                callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
            }
            return if (!predicate(this)) this else null
        }
        ```
        
        :::info
        * 使用方式就像是 `if(!...)` 的判斷
        
            ```kotlin=
            fun useIfElse2(msg: String) : String? {
                // 省略臨時變量
                val predicate = msg.length > 5

                return if (!predicate) {
                    msg
                } else
                    null
            }

            ```
        :::
        
        `takeUnless` 使用方式如下

        ```kotlin=
        fun useTaskUnless(msg: String) : String? {
            return msg.takeUnless {
                it.length > 5
            }?.toString()

        }


        fun main() {
            useTaskUnless("Hello World").also {
                println(it)
            }

            useTaskUnless("Hello").also {
                println(it)
            }
        }
        ```
        > ![](https://i.imgur.com/XcuBJ0c.png)


### 標準函數差異、比較

| 標準函數 | 使用 | 返回類型 | 參數 | 功能 |
| -------- | -------- | -------- | - | - |
| let | 透過對象 | 可返回別的對象 | ==it (本身)== | 常用於判空 |
| apply | 透過對象 | ==返回本身== | 無(this 接收者) | 簡化內部上下文 |
| run | 透過對象 | 可返回別的對象 | 無(this 接收者) | 簡化內部，並改變回傳類 |
| with | ==直接使用== (接收一個對象) | 可返回別的對象 | 無 | 不須對象的使用 |
| repeat | ==直接使用== (接收次數) | 無 | it | 重複相同代碼 |
| also | 透過對象 | 返回本身 | it | 對象副作用 |
| takeIf/taskUnless | 透過對象 | 返回本身 or null | it | 省略 if 判斷 & 臨時變數 |

## 更多的 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`
