[譯] Kotlin 揭祕:理解並速記 Lambda 語法

文章封面攝影:Stefan Steinbauer,來自 Unsplash前端

在奧地利旅行期間,我參觀了維也納的奧地利國家圖書館。特別是國會大廳,這個使人驚歎的空間感受就像印第安納瓊斯電影中的一些東西。房間周圍的空間是這些門被裝在架子上,很容易想象它們背後隱藏着什麼樣的祕密。java

然而,事實證實,它們只是簡單的圖書館。android

讓咱們假設咱們有一個應用程序來跟蹤庫中的書籍。有一天,咱們想知道這個系列中最長和最短的書是什麼。以後,咱們編寫代碼,容許咱們找到這兩個:ios

val shortestBook = library.minBy { it.pageCount }val longestBook = library.maxBy { it.pageCount }
複製代碼

完美!但這讓我感到疑惑,這些方法是如何工做的?it 是怎麼知道的,只是寫了 it.pageCount,到底該怎麼作呢?git

我作的第一件事就是定義 minBymaxBy,這二者都是在 Collections.kt。因爲它們幾乎徹底相同,因此讓咱們來看看 maxBy,它從 1559 行開始。github

那裏的方法是在 [Iterable](https://developer.android.com/reference/java/lang/Iterable) 接口上構建的,可是若是咱們作一個小的重寫來使用[Collection](https://developer.android.com/reference/java/util/Collection)s,也許將一些變量的重命名變的更冗長,更容易理解:後端

public inline fun <T, R : Comparable<R>> Collection<T>.maxBy(selector: (T) -> R): T? {
    if (isEmpty()) return null
    var maxElement = first()
    var maxValue = selector(maxElement)
    for (element in this) {
        val value = selector(element)
        if (maxValue < value) {
            maxElement = element
            maxValue = value
        }
    }
    return maxElement
}
複製代碼

咱們能夠看到它只是在 Collection 中獲取每一個元素,檢查來自 selector 的值是否大於它看到的最大值。若是是,則保存元素和值。最後,它返回它找到的最大元素。至關簡單。bash

然而 selector,看起來很整潔,它必須是容許咱們在上面使用 it.pageCount 的東西,因此讓咱們再看看它。ide

即便只是在這一行中,甚至還有至關多的語法糖。在這種狀況下,對於 selector: (T) -> R 來講是一個帶有單個參數 T 的函數,並返回一些類型 R 相關的返回值。函數

可行的方法是 Kotlin 包含一組名爲 FunctionN 的接口,其中 N 是它接受的參數數量。因爲咱們有一個參數,咱們能夠實現 Function1 接口,而後在咱們的代碼中使用它:

class BookSelector : Function1<Book, Int> {
   override fun invoke(book: Book): Int {
       return book.pageCount
   }
}
 
val longestBook = library.maxBy(BookSelector())
複製代碼

這無疑顯示了它的工做原理。selector 是一個 Function1,當給定 Book 時,返回一個 Int。而後,maxBy 獲取 Int 並將其與它具備的值進行比較。

順便說一句,這也解釋了爲何泛型參數 R 具備類型 R [implements] Comparable <R>。若是 R 不是 Comparable,咱們不能作 if(maxValue <value)

接下來的問題是,咱們如何從那開始,到咱們開始的一個循環?讓咱們逐步完成整個過程。

首先,代碼能夠替換爲 lambda,它已經減小了不少:

val longestBook = library.maxBy({
    it.pageCount
})
複製代碼

下一步是若是方法的最後一個參數是 lambda,咱們能夠關閉括號,而後將 lambda 添加到行的末尾,以下所示:

val longestBook = library.maxBy() {
    it.pageCount
}
複製代碼

最後,若是一個方法只接受一個 lambda 參數,咱們就能夠徹底放棄 () 方法,這會讓咱們回到初始代碼:

val longestBook = library.maxBy { it.pageCount }
複製代碼

可是等等!那個 Function1 要怎麼樣!我每次使用它時都會執行分配嗎?

這是一個很好的問題!好消息是,不,你不是。若是你再看一遍,你會看到它 maxBy 被標記爲一個 inline 函數。這在編譯期時會在源級別發生,所以雖然編譯的代碼比最初看起來的樣本多,可是沒有任何顯着的性能影響,固然也沒有對象分配。

真棒!如今,咱們不只知道圖書館中最短(也是最長)的書籍,咱們還能更好地理解 maxBy 它是如何工做的。咱們看到 Kotlin 如何使用[FunctionN](#full) lambda 的接口,以及如何將 lambda 表達式移到函數的參數列表以外。最後,咱們知道,當只有一個 lambda 參數調用函數時,能夠徹底省略一般使用的括號

查看 Google Developers 博客,瞭解更多精彩內容,敬請期待更多關於 Kotlin 的文章!

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索