Kotlin 2.1.20 中的新功能
Kotlin 2.1.20 已经发布了! 以下是它的一些最重要的功能:
K2 编译器更新: 对新的 kapt 和 Lombok plugin 的更新
Kotlin Multiplatform: 替代 Gradle 的 Application plugin 的新 DSL
Kotlin/Native: 支持 Xcode 16.3, 以及新的内联(inlining)优化
Kotlin/Wasm: 默认使用自定义格式(Custom Formatter), 支持 DWARF, 以及迁移到 Provider API
Gradle 支持: 兼容 Gradle 的隔离项目(Isolated Project)和自定义发布变体(Publication Variant)
Compose 编译器: 放宽了
@Composable
函数的限制, 以及其它更新文档: Kotlin 文档的一些重要改进.
IDE 支持
最新版的 IntelliJ IDEA 和 Android Studio 中绑定了支持 2.1.20 的 Kotlin plugin. 你不需要在你的 IDE 中更新 Kotlin plugin. 你需要做的只是在你的构建脚本中将 Kotlin 版本修改为 2.1.20.
详情请参见 更新到新的发布版.
在支持 OSGi 的项目中下载 Kotlin artifact 源代码
kotlin-osgi-bundle
库的所有依赖项的源代码现在都包含在发行版中. 因此 IntelliJ IDEA 可以下载这些源代码, 提供 Kotlin 符号的文档, 并改善调试体验.
Kotlin K2 编译器
我们一直在持续的改进对新的 Kotlin K2 编译器的 plugin 支持. 这个发布版带来了对新的 kapt 和 Lombok plugin 的更新.
默认启用新的 kapt plugin
从 Kotlin 2.1.20 开始, 对所有的项目默认启用 kapt 编译器 plugin 的 K2 实现.
JetBrains 开发组早在 Kotlin 1.9.20 中就发布了与 K2 编译器配合工作的 kapt plugin 的新实现. 之后, 我们进一步开发了 K2 kapt 的内部实现, 并让它的行为接近于 K1 版本, 同时显著的提升了它的性能.
如果你在使用 kapt 和 K2 编译器时遇到任何问题, 可以暂时回退到之前的 plugin 实现.
方法是, 向你的项目的 gradle.properties
文件添加以下选项:
如果遇到问题, 请在我们的 问题追踪系统 中提交报告.
Lombok 编译器 plugin: 支持 @SuperBuilder
, 以及对 @Builder
的更新
Kotlin Lombok 编译器 plugin 现在支持 @SuperBuilder
注解, 可以更加容易的为类的层级结构创建构建器. 之前, 在 Kotlin 中使用 Lombok 的开发者, 在使用类的层级结构时必须手动定义构建器. 通过 @SuperBuilder
, 构建器会自动继承父类的域变量, 你可以在构建对象时初始化它们.
此外, 这个更新还包含几个改进和 bug 修正:
@Builder
注解现在可以用于构造器, 能够更加灵活的创建对象. 详情请参见对应的 YouTrack issue.修正了与 Kotlin 中 Lombok 代码生成相关的几个问题, 改进了整体的兼容性. 详情请参见 GitHub changelog.
关于 @SuperBuilder
注解, 详情请参见官方的 Lombok 文档.
Kotlin Multiplatform: 替代 Gradle 的 Application plugin 的新 DSL
从 Gradle 8.7 开始, Application plugin 与 Kotlin Multiplatform Gradle plugin 不再兼容. Kotlin 2.1.20 引入了实验性的 DSL, 来实现类似的功能. 新的 executable {}
代码段会为 JVM 编译目标配置执行 task, 以及 Gradle 发布.
在你的构建脚本的 executable {}
代码段之前, 请添加以下 @OptIn
注解:
例如:
在这个示例中, Gradle 的 Distribution plugin 会被适用于第一个 executable {}
代码段.
如果遇到问题, 请在我们的 问题追踪系统 中提交报告, 或在我们的 公开 Slack 频道 中通知我们.
Kotlin/Native
支持 Xcode 16.3
从 Kotlin 2.1.21 开始, Kotlin/Native 编译器支持 Xcode 16.3 – Xcode 的最新稳定版. 你可以更新你的 Xcode, 继续在 Apple 操作系统上开发你的 Kotlin 项目.
2.1.21 发布版还修正了相关的 cinterop 问题, 这个问题会导致 Kotlin Multiplatform 项目编译失败.
新的内联(inlining)优化
Kotlin 2.1.20 引入了新的内联优化过程, 这个过程发生在实际的代码生成阶段之前.
Kotlin/Native 编译器中的新的内联过程应该比标准的 LLVM 内敛器表现更好, 并能够改进生成的代码的运行期性能.
新的内联过程目前是 实验性功能. 要试用这个功能, 请使用以下编译器选项:
我们的实验显示, 将阈值设置为 40 个 token (由编译器解析的代码单元) 可以为编译优化提供合理的折衷方案. 根据我们的基准测试, 这个设置可以让整体性能提升 9.5%. 当然, 你也可以尝试其它阈值设置.
如果你遇到二进制文件尺寸变大, 或编译时间变长的问题, 请在 YouTrack 中提交报告.
Kotlin/Wasm
这个发布版改进了 Kotlin/Wasm 的调试和属性使用. 自定义格式(Custom Formatter)现在可以在开发构建中开箱即用, DWARF 调试功能改进了代码检查. 此外, Provider API 简化了 Kotlin/Wasm 和 Kotlin/JS 中的属性使用.
默认启用自定义格式(Custom Formatter)
之前, 在开发 Kotlin/Wasm 代码时, 你必须 手动配置 自定义格式(Custom Formatter) 来改善 Web 浏览器中的调试.
在这个发布版中, 在开发构建中默认启用自定义格式, 因此你不需要进行额外的 Gradle 配置.
要使用这个功能, 你只需要在你的浏览器的开发者工具中确保启用了自定义格式:
在 Chrome DevTools 中, 请在 Settings | Preferences | Console 中找到 Custom formatters 选择框:
在 Firefox DevTools 中, 请在 Settings | Advanced settings 找到 Custom formatters 选择框:
这个变更主要影响 Kotlin/Wasm 的开发构建(Development Build). 如果对生产构建(Production Build)有特定的要求, 你需要相应的调整你的 Gradle 配置. 方法是, 向 wasmJs {}
代码段添加以下编译器选项:
支持 DWARF 调试 Kotlin/Wasm 代码
Kotlin 2.1.20 在 Kotlin/Wasm 中引入了对 DWARF (debugging with arbitrary record format) 的支持.
通过这个变更, Kotlin/Wasm 编译器能够将 DWARF 数据嵌入到生成的 WebAssembly (Wasm) 二进制文件中. 很多调试器和虚拟机能够读取这个数据, 深入了解编译后的代码.
DWARF 主要用于在独立的 Wasm 虚拟机 (VM) 中调试 Kotlin/Wasm 应用程序. 要使用这个功能, Wasm VM 和调试器必须支持 DWARF.
通过支持 DWARF, 你可以单步执行 Kotlin/Wasm 应用程序, 查看变量, 以及检查代码. 要启用这个功能, 请使用以下编译器选项:
迁移到 Kotlin/Wasm 和 Kotlin/JS 属性的 Provider API
之前, Kotlin/Wasm 和 Kotlin/JS 扩展中的属性必须是可变的 (var
), 并在构建脚本中直接赋值:
现在, 属性通过 Provider API 公开, 你必须使用 .set()
函数来赋值:
Provider API 确保值被延迟计算, 并与 task 依赖项正确的集成, 改进构建性能.
通过这个变更, 对属性直接赋值已被废弃, 请改用 *EnvSpec
类, 例如 NodeJsEnvSpec
和 YarnRootEnvSpec
.
此外, 还删除了几个别名 task, 以避免混淆:
废弃的 task | 替代者 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
如果你在构建脚本中只使用 Kotlin/JS 或 Kotlin/Wasm, 那么不必进行任何行动, 因为 Gradle 会自动处理赋值.
但是, 如果你在维护基于 Kotlin Gradle Plugin 的 plugin, 而且你的 plugin 没有适用 kotlin-dsl
, 那么你必须更新属性赋值语句, 改为使用 .set()
函数.
Gradle
Kotlin 2.1.20 完全兼容于 Gradle 版本 7.6.3 到 8.11. 你也可以使用 Gradle 的最新版本. 但请注意, 这样可能会导致废弃警告, 以及一些新的 Gradle 特性可能无法工作.
这个版本的 Kotlin 包括, Kotlin Gradle plugin 兼容 Gradle 的隔离项目(Isolated Project), 以及对自定义 Gradle 发布变体(Publication Variant)的支持.
Kotlin Gradle plugin 兼容 Gradle 的隔离项目(Isolated Project)
从 Kotlin 2.1.0 开始, 你可以在你的项目中 预览使用 Gradle 的隔离项目功能.
之前, 你必须配置 Kotlin Gradle plugin 来让你的项目兼容于隔离项目功能, 然后才能试用这个功能. 在 Kotlin 2.1.20 中, 不再需要这个额外的步骤了.
现在, 要启用隔离项目功能, 你只需要 设置系统属性.
Kotlin Gradle plugin 插件支持的 Gradle 的隔离项目功能, 可以用于跨平台项目和只包含 JVM 或 Android 编译目标的项目.
特别是对于跨平台项目, 如果升级后在你的 Gradle 构建中发现问题, 你可以选择关闭新的 Kotlin Gradle plugin 行为, 方法是添加以下设定:
但是, 如果在你的跨平台项目中使用这个 Gradle 属性, 你将无法使用隔离项目功能.
请在 YouTrack 中分享你使用这个功能的体验.
支持添加自定义的 Gradle 发布变体(Publication Variant)
Kotlin 2.1.20 引入了对添加自定义 Gradle 发布变体(Publication Variant) 的支持. 这个功能可以用于跨平台项目和 JVM 平台项目.
这个功能是 实验性功能. 要选择使用者同意, 请使用 @OptIn(ExperimentalKotlinGradlePluginApi::class)
注解.
要添加一个自定义的 Gradle 发布变体, 请调用 adhocSoftwareComponent()
函数, 它返回一个 AdhocComponentWithVariants
实例, 你可以在 Kotlin DSL 中对它进行配置:
标准库
这个发布版为标准库带来了新的实验性功能: 共通的原子类型, 改进了对 UUID 的支持, 以及新的时间追踪功能.
共通的原子类型
在 Kotlin 2.1.20 中, 我们在标准库的 kotlin.concurrent.atomics
包中引入了共通的原子类型, 对线程安全的操作, 支持共用的, 平台独立的代码. 这样能够在各个数据集中消除重复的原子操作相关的代码逻辑, 简化 Kotlin 跨平台项目的开发.
kotlin.concurrent.atomics
包, 以及它的属性是 实验性功能. 要选择使用者同意, 请使用 @OptIn(ExperimentalAtomicApi::class)
注解, 或编译器选项 -opt-in=kotlin.ExperimentalAtomicApi
.
下面是一个示例, 演示如何使用 AtomicInt
, 跨越多个线程安全的对已处理的项目进行计数:
为了实现 Kotlin 的原子类型和 Java 的 java.util.concurrent.atomic
原子类型之间的无缝集成, API 提供了 .asJavaAtomic()
和 .asKotlinAtomic()
扩展函数. 在 JVM 平台, Kotlin 原子类型和 Java 原子类型在运行期是相同的类型, 因此你可以将 Java 原子类型转换为 Kotlin 原子类型, 或者反过来, 不会发生任何开销.
下面是一个示例, 演示 Kotlin 和 Java atomic 原子类型如何协同工作:
UUID 解析, 格式化, 以及兼容性的变更
JetBrains 开发组在持续的改进对 在 2.0.20 中引入到标准库 的 UUID 的支持.
之前, parse()
函数只支持 "16进制-横线" 格式的 UUID. 在 Kotlin 2.1.20 中, parse()
能够 同时 处理 "16进制-横线" 和 "纯16进制" (没有横线) 格式.
在这个发布版中, 我们还引入了专用于操作 "16进制-横线" 格式的函数:
parseHexDash()
, 从 "16进制-横线" 格式解析 UUID.toHexDashString()
, 将一个Uuid
转换为String
, 使用 "16进制-横线" 格式 (与toString()
的功能完全相同).
这些函数类似于之前版本引入的, 处理16进制格式的 parseHex()
和 toHexString()
函数. 对解析和格式化功能明确命名, 可以改善代码的清晰度以及你使用 UUID 时的整体体验.
Kotlin 中的 UUID 现在是 Comparable
类型. 从 Kotlin 2.1.20 开始, 你可以直接对 Uuid
类型的值进行比较和排序. 因此可以使用 <
和 >
操作符, 以及标准库中专用于 Comparable
类型或 Comparable
类型构成的集合的扩展 (例如 sorted()
), 而且可以将 UUID 传递给接口中要求 Comparable
类型的任何函数或 API.
请记住, 标准库中的 UUID 支持功能还是 实验性功能. 要选择使用者同意, 请使用 @OptIn(ExperimentalUuidApi::class)
注解, 或编译器选项 -opt-in=kotlin.uuid.ExperimentalUuidApi
:
新的时间追踪功能
从 Kotlin 2.1.20 开始, 标准库提供了表示某个时刻的功能. 这个功能只存在于 kotlinx-datetime
中, 这是一个官方的 Kotlin 库.
kotlinx.datetime.Clock
接口现在引入到标准库中, 成为 kotlin.time.Clock
, kotlinx.datetime.Instant
类成为 kotlin.time.Instant
. 这些概念自然的与标准库中的 time
包保持一致, 因为它们只关注某个时刻, 更复杂的日历和时区功能继续保留在 kotlinx-datetime
中.
如果你需要精确的时间追踪, 而不考虑时区或日期, Instant
和 Clock
会非常有用. 例如, 你可以使用它们记录带时间戳的事件, 测量两个时间点之间的时长, 以及获取系统进程的目前时刻.
为了提供与其它语言的交互能力, 还提供了额外的转换函数:
.toKotlinInstant()
将一个时间值转换为kotlin.time.Instant
的实例..toJavaInstant()
将kotlin.time.Instant
值转换为java.time.Instant
值.Instant.toJSDate()
将kotlin.time.Instant
值转换为 JSDate
类的实例. 这个转换并不精确; JS 使用毫秒精度表示日期, 而 Kotlin 可以使用纳秒精度.
标准库的新的时间功能还是 实验性功能. 要选择使用者同意, 请使用 @OptIn(ExperimentalTime::class)
注解:
关于具体实现, 请参见这个 KEEP 提案.
Compose 编译器
在 2.1.20 中, Compose 编译器放宽了之前版本中引入的 @Composable
函数的一些限制. 此外, Compose 编译器 Gradle plugin 默认设置为包含源代码信息, 使所有平台的行为与 Android 保持一致.
支持 open 的 @Composable
函数中的默认参数
之前, 编译器限制了 open 的 @Composable
函数中的默认参数, 原因是编译器输出不正确, 会导致运行期崩溃. 底层的问题现在已经解决, 在与 Kotlin 2.1.20 或更高版本一起使用时, 完全支持默认参数.
Compose 编译器在 版本 1.5.8 之前, 允许使用 open 的函数中的默认参数, 因此这种支持依赖于项目配置:
如果一个 open 的 composable 函数使用 Kotlin version 2.1.20 或更高版本编译, 编译器会为默认参数生成正确的包装器. 包括与 1.5.8 之前版本二进制文件兼容的包装器, 这就意味着下游库也能够使用这个 open 函数.
如果 open 的 composable 函数使用 Kotlin 2.1.20 之前的版本编译, Compose 会使用兼容模式, 可能导致运行期崩溃. 使用兼容模式时, 编译器会发出警告, 标记出潜在的问题.
final 覆盖函数允许重新启动
虚函数 (对 open
和 abstract
的覆盖, 包括接口) 从 2.1.0 版开始强制为不可重新启动. 对于 final 类的成员函数, 或本身为 final
的函数, 这个限制现在已经放宽了 – 它们将象通常那样重新启动, 或跳过.
升级到 Kotlin 2.1.20 之后, 你可能会看到受影响的函数的一些行为发生了变化. 如果要强制使用之前版本的不可重新启动逻辑, 请对函数使用 @NonRestartableComposable
注解.
从 public API 中删除了 ComposableSingletons
ComposableSingletons
是 Compose 编译器在优化 @Composable
Lambda 表达式时创建的类. 不捕获任何参数的 Lambda 表达式只分配一次, 并缓存在这个类的一个属性中, 以节约运行期的分配. 这个类生成时可见度为 internal, 只用于在一个编译单元(通常是一个文件)中优化 Lambda 表达式.
但是, 这个优化也应用于 inline
函数的 body 部, 导致单体 Lambda 表达式实例泄露到 public API 中. 为了解决这个问题, 从 2.1.20 开始, @Composable
Lambda 表达式不再被优化为内联函数中的单体. 同时, Compose 编译器会继续为内联函数生成单体类和 Lambda 表达式, 以支持在之前模式下编译的模块的二进制兼容性.
默认包含源代码信息
Compose 编译器 Gradle plugin 在 Android 中已经默认启用了 包含源代码信息 功能. 从 Kotlin 2.1.20 开始, 这个功能会对所有的平台默认启用.
请记得检查是否使用 freeCompilerArgs
设置了这个选项. 这个方法在与 plugin 一起使用时, 可能导致构建失败, 因为选项实际上被设置了两次.
破坏性变更和废弃
为了让 Kotlin Multiplatform 与 Gradle 中即将推出的变更保持一致, 我们会逐步废弃
withJava()
函数. 现在会默认创建 Java 源代码集. 如果你使用 Java test fixtures Gradle plugin, 请直接升级到 Kotlin 2.1.21, 以免发生兼容性问题.JetBrains 开发组正在逐步废弃
kotlin-android-extensions
plugin. 如果试图在你的项目中使用它, 现在会发生配置错误, 不会执行任何 plugin 代码.旧的
kotlin.incremental.classpath.snapshot.enabled
属性已从 Kotlin Gradle plugin 中删除. 过去这个属性用来在 JVM 上提供回退到内建的 ABI 快照的功能. plugin 现在使用其它方法来检测, 并避免不必要的重编译, 因此淘汰了这个属性.
文档更新
Kotlin 文档有了一些重要更新:
改版和新增页面
Kotlin 发展路线图 – 查看 Kotlin 在语言和生态系统演化方面的优先事项最新列表.
Gradle 最佳实践 页面 – 学习关于优化 Gradle 构建和改善性能的基本的最佳实践.
Compose Multiplatform 与 Jetpack Compose – 关于这两个 UI 框架之间关系的概述.
Kotlin Multiplatform 与 Flutter – 查看这两个流行的跨平台框架的比较.
与 C 代码交互 – 探索 Kotlin 与 C 交互的细节.
数值类型 – 了解用于表示数值的各种 Kotlin 类型.
新的和更新的教程
将你的库发布到 Maven Central – 学习如何将 KMP 库 artifact 发布到最流行的 Maven 仓库.
使用 Kotlin/Native 开发动态库 – 创建动态 Kotlin 库.
使用 Kotlin/Native 开发 Apple Framework – 创建你的自己的 Framework, 并在 macOS 和 iOS 上的 Swift/Objective-C 应用程序中使用 Kotlin/Native 代码.
如何更新到 Kotlin 2.1.20
从 IntelliJ IDEA 2023.3 和 Android Studio Iguana (2023.2.1) Canary 15 开始, Kotlin plugin 作为一个包含在 IDE 中的捆绑 plugin 发布. 这意味着你不再能够通过 JetBrains Marketplace 安装这个 plugin.
要更新到新的 Kotlin 版本, 请在你的构建脚本中 变更 Kotlin 版本到 2.1.20.