Twitter 上有趣的程式碼

語言: CN / TW / HK

Hi 大家好,我是 DHL。公眾號:ByteCode ,專注分享有趣硬核原創內容,Kotlin、Jetpack、效能優化、系統原始碼、演算法及資料結構、動畫、大廠面經

全文分為 影片版文字版

  • 文字版: 文字側重細節和深度,有些知識點,影片不好表達,文字描述的更加準確
  • 影片版: 影片以動畫的形式會更加的直觀,看完文字版,在看影片,知識點會更加清楚,Twitter 上有趣的程式碼_嗶哩嗶哩_bilibili

這是海外一位 Kotlin GDE 大佬,在 Twitter 上分享的一段程式碼,我覺得非常的有意思,程式碼如下所示,我們花 10s 思考一下,輸出結果是什麼。

```kotlin fun printE() = { println("E") }

fun main() { if (true) println("A") if (true) { println("B") } if (true) { { println("C") } }

{ println("D") }

printE()

when {
    true -> { println("F") }
}

} ```

在 Twitter 評論區中也能看到很多不同的答案。

pic02

實際上最後輸出結果如下所示。

kotlin A B F

不知道你第一次看到這麼多混亂的花括是什麼感覺,當我第一次看到這段程式碼的時候,我覺得非常的有意思。

如果在實際專案中有小夥伴這麼巢狀花括號,我相信肯定會被拉出去暴晒。但是細心觀察這段程式碼,我們能學習到很多 Kotlin 相關的知識點,我們先來說一下為什麼最後輸出的結果是 A B F

下面圖中紅色標註部分,if 表示式、 when ... case 表達,如果表示式內只有一行程式碼的話,花括號是可以省略的,程式執行到程式碼位置會輸出對應的結果, 即 A B F

那為什麼 C D E 沒有列印,因為圖中綠色部分是 lambda 表示式,在 Kotlin 中 lambda 表示式非常的自由,它可以出現在很多地方比如方法內、 if 表示式內、迴圈語句內、甚至賦值給一個變數、或者當做方法引數進行傳遞等等。

lambda 表示式用花括號包裹起來,用箭頭把實參列表和 lambda 函式體分離開來,如下所示。

kotlin { x: Int -> println("lambda 函式體") }

如果沒有引數,上面的程式碼可以簡寫成下面這樣。

kotlin { println("lambda 函式體") }

C D E 的輸出語句在 lambda 函式體內, lambda 表示式我們可以理解為高階函式,在上面的程式碼中只是聲明瞭這個函式,但是並沒有呼叫它,因此不會執行,自然也就不會有任何輸出。現在我將上面的程式碼做一點點修改,在花 10s 思考一下輸出結果是什麼。

```kotlin fun printE() = { println("E") }

fun main() { if (true) println("A") if (true) { println("B") } if (true) { { println("C") }() }

{ println("D") }()

printE()()

when {
    true -> { println("F") }
}

} ```

最後的輸出結果是:

kotlin A B C D E F

應該有小夥伴發現了我做了那些修改,我只是在 lambda 表示式後面加了一個 (),表示執行當前的 lambda 表示式,所以我們能看到對應的輸出結果。如下圖所示,

lambda 表示式最終會編譯成 FunctionN 函式,如下圖所示。

如果沒有引數會編譯成 Function0,一個引數編譯成 Function1,以此類推。FunctionN 過載了操作符 invoke。如下圖所示。

因此我們可以呼叫 invoke 方法來執行 lambda 表示式。

kotlin { println("lambda 函式體") }.invoke()

當然 Kotlin 也提供了更加簡潔的方式,我們可以使用 () 來代替 invoke(),最後的程式碼如下所示。

kotlin { println("lambda 函式體") }()

到這裡我相信小夥伴已經明白了上面程式碼輸出的結果,但是這裡隱藏了一個有效能損耗的風險點,分享一段我在實際專案中見到的程式碼,示例中的程式碼,我做了簡化。

```kotlin fun main() {

(1..10).forEach { value ->
    calculate(value) { result ->
        println(result)
    }
}

}

fun calculate(x: Int, lambda: (result: Int) -> Unit) { lambda(x + 10) } ```

上面的程式碼其實存在一個比較嚴重的效能問題,我們看一下反編譯後的程式碼。

每次在迴圈中都會建立一個 FunctionN 的物件,那麼如何避免這個問題,我們可以將 lambda 表示式放在迴圈之外,這樣就能保證只會建立一個 FunctionN 物件,我們來看一下修改後的程式碼。

```kotlin fun main() { val lambda: (result: Int) -> Unit = { result -> println(result) }

(1..10).forEach { value ->
    calculate(value, lambda)
}

} ```


全文到這裡就結束了,感謝你的閱讀,堅持原創不易,歡迎在看、點贊、分享給身邊的小夥伴,我會持續分享原創乾貨!!!

真誠推薦你關注我,公眾號:ByteCode ,持續分享硬核原創內容,Kotlin、Jetpack、效能優化、系統原始碼、演算法及資料結構、動畫、大廠面經。



近期必讀熱門文章

最後推薦長期更新和維護的專案

  • 個人部落格,將所有文章進行分類,歡迎前去檢視 https://hi-dhl.com

  • KtKit 小巧而實用,用 Kotlin 語言編寫的工具庫,歡迎前去檢視 KtKit

  • 計劃建立一個最全、最新的 AndroidX Jetpack 相關元件的實戰專案以及相關元件原理分析文章,正在逐漸增加 Jetpack 新成員,倉庫持續更新,歡迎前去檢視 AndroidX-Jetpack-Practice

  • LeetCode / 劍指 offer / 國內外大廠面試題 / 多執行緒題解,語言 Java 和 kotlin,包含多種解法、解題思路、時間複雜度、空間複雜度分析