抢先体验 Kotlin 1.4-M2:标准库改进

发布于 JetBrains_CN

我们已准备好在此预览版中引入一些改进;在这篇博文中,我们将介绍标准库的变化。

以下是 1.4-M2 中标准库的一些主要改进:

尽管 Kotlin 1.4-M2 尚未发布,但我们已将其早期版本部署到 Kotlin 演练场中,您可以尝试本文中介绍的所有功能。本文中的代码示例也可以在新版本上运行。

如果您迫不及待地想要体验新地版本,订阅我们的 Kotlin 博客新闻通讯,不必担心错过发布的消息!

扩展通用库

您可以在“通用”代码中使用标准库,“通用”代码是指在不同的平台(即 Android 与 iOS 或 JVM 与 JS)之间共享的代码。我们将逐步扩展通用库,并向其添加或引入缺少的功能。

之前appendln 在 Kotlin/JVM 中的上一个实现添加了一个因系统而异的行分隔符(UNIX 系统上为 \n;Windows 上为 \r\n)。不过,既然是通用代码,我们认为无论操作系统和底层平台如何,确保一致的行为非常重要。

因此,我们弃用了 appendln 并采用新的 appendLine 函数,后者始终使用一个 \n 字符终止行:

如果您需要平台特定的分隔符,仍可以在 JVM 中使用 System.lineSeparator()

现在可以在通用代码中使用的其他函数让您能够使用给定堆栈跟踪的字符串表示。Throwable.stackTraceToString() 扩展可以返回此 Throwable 及其堆栈跟踪的详细说明Throwable.printStackTrace() 可以将此说明打印到标准错误输出。

在通用代码中,您还可以使用 Throwable.addSuppressed() 函数(让您可以指定之前被抑制的异常以提供异常)和 Throwable.suppressedExceptions 属性(可以返回所有被抑制异常的列表)。

新的数组函数

为了在处理不同的容器类型时提供一致的体验,我们为数组添加了新的扩展函数:

  • shuffle() – 以随机顺序放置数组元素。
  • onEach() – 在每个数组元素上执行给定操作并返回数组本身。

使用列表的用户应当非常熟悉这些函数;它们以相同的方式适用于数组。

我们还添加了用于对数组子范围进行排序的新函数。之前,有一个包含 fromIndex 和 toIndex 参数的 sort(),但它仅适用于 JVM。现在,它在各个平台上都通用,还有两个新的相关函数,它们是子范围版本的 reverse() 和 sortDescending()。每一个都需要两个索引,并以从名称即可看出的方式在它们之间重新排序元素(包括 fromIndex,但不包括 toIndex):

  • 为数组子范围使用 reverse() 可以反转元素在子范围中的顺序。
  • 为数组子范围使用 sortDescending() 会在子范围中以降序排列元素。

集合 API 中的新函数

在 1.4-M2 中,我们继续在标准库中扩展集合 API 以涵盖更多实际用例:

    • 新的集创建函数 setOfNotNull() 可以让集包括所提供参数中的所有非空项。

shuffled() 现在可用于序列。

      • onEachIndexed() 和 reduceIndexedOrNull() 分别为 onEach() 和 reduceOrNull() 的对应项添加。您可能已经知道,集合处理函数名称中的 Indexed 表示应用的操作将元素索引作为参数。
    • runningFold() 和 runningReduce() 作为 scan() 和 scanReduce() 的同义项引入。此类名称与相关函数 fold() 和 reduce() 更一致。未来,scan() 与 runningFold() 都会使用,因为前者是此操作众所周知的名称。不过,实验性 scanReduce() 将被弃用,并且很快将被移除。

改进现有 API

由于 Kotlin 1.4 是一个主要的“功能”版本,我们可以向该语言中添加新功能,以及向标准库中添加新函数或接口。我们在增量版本(例如 1.3.70)中仅添加了新的实验性声明。如果您目前使用 Kotlin 1.3.70 编写代码并且没有使用任何实验性声明,使用 Kotlin 1.3.40 的同事可以正常编译您的代码。

未来版本不需要与次要版本遵循相同的严格规则,也就是说,如果您使用新功能或 API,1.3 版的 Kotlin 编译器可能无法编译使用 Kotlin 1.4 编写的代码。这让我们可以在 API 中引入一些变更来进行改进。我们确实在仔细观察向后兼容性,以便让您为旧版本编写的代码可以向之前一样编译和运行。
在 Kotlin 1.4 中,我们放宽了多个函数,使其可以接受 null

Kotlin 1.3 将不会编译此代码,因为它需要 String.toBoolean() 的接收器不可为空。Kotlin 1.4 将该接收器更改为可空字符串:String?.toBoolean()。您之前编写的所有代码在 Kotlin 1.4 中仍可以编译和运行。
相同的逻辑同样适用于 contentEqualscontentHashCode 和 contentToString 函数的 Array 接收器:它现在可以为空。
此外,String.format() 现在允许 null 作为 locale 参数,在这种情况下,不会应用本地化。

以 Double 和 Float 类型定义的以下常量现在是“真正的”常量:

它们现在定义为 const 变量,因此,您可以将其用作注解参数。
SIZE_BITS 和 SIZE_BYTES 现在是 Double 和 Float 类型的新常量;它们分别包含用于以二进制形式表示类型实例的位数和字节数。
请注意,我们还在 Kotlin/JS 中更改了 Float.MAX_VALUE 和 Float.MIN_VALUE 值。之前,它们等于 JavaScript Number.MAX/MIN_VALUE(相当于 Double.MAX/MIN_VALUE),因为在 Kotlin/JS 中,Float 本质上等于 Double。现在,这些 Float 范围常量在所有平台中都相同。

带 vararg 的 maxOf() 和 minOf()

标准库中的 maxOf() 和 minOf() 函数可以查找两个值中的较大者和较小者。从 1.4-M1 开始,maxOf() 和 minOf() 可以接受可变数量的参数 (vararg),这样一来,您可以将其用于任意数字组或其他可比项组。

属性委托改进

在 Kotlin 中,委托的属性通过惯例而不是接口运行:您想要用作委托的类型必须定义运算符函数,而不是实现所需接口。这提供了灵活性(因为我们不受特定接口的限制),但在许多实际用例中,使用接口仍然有用。
在 Kotlin 1.4 中,我们提升了此类补充性接口的适用性:我们引入了新的 PropertyDelegateProvider 接口,ReadWriteProperty 现在继承 ReadOnlyProperty。请继续阅读以了解更多详细信息。

委托表达式

ReadWriteProperty 和 ReadOnlyProperty 接口可用于定义属性委托,因为您的自定义类或匿名对象可以实现它们:

从 1.4 开始,ReadWriteProperty 继承 ReadOnlyProperty。这让您可以更灵活地使用委托表达式。在我们的示例中,现在每当应该出现 ReadOnlyProperty 时,您就可以传递 myDelegate() 调用。
在这里我们想强调一下,“只读”与 Kotlin 中的“不可变”不同,类似于只读列表并不是不可变列表。“只读”的意思是“此接口仅提供对相关对象的只读访问权限”。

提供委托

使用提供委托的机制,您可以扩展创建“委托”对象(属性实现所委托到的对象)的逻辑。
您可以在文档中找到其工作原理的详细信息。在 1.4 中,为了让此机制更加方便,我们添加了新的 PropertyDelegateProvider 接口。如果您不想额外创建类并倾向于使用匿名对象,可以使用该接口,类似于我们在上面的 myDelegate() 示例中看到的一样。

委托给另一个属性

从 1.4 开始,一个属性可以直接将其 getter 和 setter 委托给另一个属性。如果您想以向后兼容的方式移除某个属性,这样可能很有用:引入一个新属性,使用 @Deprecated 注解旧属性,然后委托其实现。

之前介绍的对已编译委托属性的优化适用于这种情况。由于委托运算符实现不使用正在委托的属性的相关信息 (oldName),编译器不需要生成包含该信息的 KProperty 实例。未来,您还可以不用为委托生成额外的 KMutableProperty 实例 (newName) 。

如何试用

本文介绍的所有变化都会体现在 Kotlin 1.4-M2 版本中,不过,您现在就可以在 play.kotl.in 上在线试用。

预发布说明

请注意,后向兼容性保证不涵盖预发布版本。功能和 API 在后续版本中可能根据用户反馈进行更改。

分享您的反馈

感谢大家在我们的问题跟踪器中提交错误报告。

我们将尽力在最终版本之前修复所有重要问题,因此,您不用等到下一个 Kotlin 版本即可看到问题得到解决。

如果您想提问,参与讨论以及获取新的预览版本的通知,欢迎加入我们在 Kotlin Slack 中的 \#eap 频道(在此处获取邀请)。

Let’s Kotlin!