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,包含多種解法、解題思路、時間複雜度、空間複雜度分析