[译] Kotlin 揭秘:理解并速记 Lambda 语法

文章封面摄影:Stefan Steinbauer,来自 Unsplash

在奥地利旅行期间,我参观了维也纳的奥地利国家图书馆。特别是国会大厅,这个令人惊叹的空间感觉就像印第安纳琼斯电影中的一些东西。房间周围的空间是这些门被装在架子上,很容易想象它们背后隐藏着什么样的秘密。

然而,事实证明,它们只是简单的图书馆。

让我们假设我们有一个应用程序来跟踪库中的书籍。有一天,我们想知道这个系列中最长和最短的书是什么。之后,我们编写代码,允许我们找到这两个:

val shortestBook = library.minBy { it.pageCount }val longestBook = library.maxBy { it.pageCount }
复制代码

完美!但这让我感到疑惑,这些方法是如何工作的?it 是怎么知道的,只是写了 it.pageCount,到底该怎么做呢?

我做的第一件事就是定义 minBymaxBy,这两者都是在 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 链接。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器之心

PyTorch为何如此高效好用?来探寻深度学习框架的内部架构

选自blog.christianperone 作者:Christian S. Perone 机器之心编译 参与:思源、黄小天、李泽南 作为 Facebook 人...

41760
来自专栏阿杜的世界

《重构》阅读笔记-代码的坏味道

开发者必须通过实践培养自己的经验和直觉,培养出自己的判断力:学会判断一个类内有多少个实例变量算是太大、学会判断一个函数内有多少行代码才算太长。

7820
来自专栏即时通讯技术

字符编码那点事:快速理解ASCII、Unicode、GBK和UTF-8

原作者:阮一峰(ruanyifeng.com),现重新整理发布,感谢原作者的无私分享。

28320
来自专栏Java技术栈

跟我学 Java 8 新特性之 Stream 流(四)并行流

在开始讨论并行流之前,我先引发一下大家的思考,就你看到这篇文章的时间,你们是不是经常听到,Intel i7 CPU什么8核16线程,什么Android手机8核4...

10320
来自专栏java一日一条

哪些因素影响Java调用的性能?

这得从一个小故事说起。我在一个Java核心库的邮件列表中提交了一个修改 ——重写了一些本是 final 的方法。一石激起千层浪,这一改动引发了几番讨论。而其中一...

13910
来自专栏xingoo, 一个梦想做发明家的程序员

通过两个小栗子来说说Java的sleep、wait、notify、notifyAll的用法

线程是计算程序运行的最小载体,由于单个单核CPU的硬件水平发展到了一定的瓶颈期,因此就出现了多核多CPU的情况,直接就导致程序员多线程编程的复杂。由此可见线程...

25480
来自专栏java工会

深度思考编程的艺术

18280
来自专栏码洞

编程的智慧

编程是一种创造性的工作,是一门艺术。精通任何一门艺术,都需要很多的练习和领悟,所以这里提出的“智慧”,并不是号称一天瘦十斤的减肥药,它并不能代替你自己的勤奋。然...

6410
来自专栏怀英的自我修炼

Java漫谈2

本周我们的Java漫谈从一个段子说起。话说有一个老程序退休了,在家闲着没事便开始学习写毛笔字,焚香,研墨,铺纸。站在薄如蝉翼白似雪的宣纸面前,提笔闭目。只见那人...

34480
来自专栏大数据文摘

业界 | 用Python做数据科学时容易忘记的八个要点!

虽然我们在StackOverflow或其他网站上查找答案是很正常的事情,但这样做确实比较花时间,也让人怀疑你是否完全理解了这门编程语言。

13000

扫码关注云+社区

领取腾讯云代金券