- 原文地址:Kotlin Demystified: Understanding Shorthand Lambda Syntax
- 原文作者:Nicole Borrelli
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:androidxiao
在奧地利旅行期間,我參觀了維也納的奧地利國家圖書館。特別是國會大廳,這個令人驚歎的空間感覺就像印第安納瓊斯電影中的一些東西。房間周圍的空間是這些門被裝在架子上,很容易想象它們背後隱藏著什麼樣的祕密。
然而,事實證明,它們只是簡單的圖書館。
讓我們假設我們有一個應用程式來跟蹤庫中的書籍。有一天,我們想知道這個系列中最長和最短的書是什麼。之後,我們編寫程式碼,允許我們找到這兩個:
val shortestBook = library.minBy { it.pageCount }val longestBook = library.maxBy { it.pageCount }
複製程式碼
完美!但這讓我感到疑惑,這些方法是如何工作的?it
是怎麼知道的,只是寫了 it.pageCount
,到底該怎麼做呢?
我做的第一件事就是定義 minBy
和 maxBy
,這兩者都是在 Collections.kt。由於它們幾乎完全相同,所以讓我們來看看 maxBy
,它從 1559 行開始。
那裡的方法是在 [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
的值是否大於它看到的最大值。如果是,則儲存元素和值。最後,它返回它找到的最大元素。相當簡單。
然而 selector
,看起來很整潔,它必須是允許我們在上面使用 it.pageCount
的東西,所以讓我們再看看它。
即使只是在這一行中,甚至還有相當多的語法糖。在這種情況下,對於 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 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。