13.6.1.1 Kotlin 2.3.20 新变化

原文链接: https://kotlinlang.org/docs/whatsnew2320.html

13.6.1.1 Kotlin 2.3.20 新变化

阅读 Kotlin 2.3.20 发行说明,了解新的语言特性,以及 Kotlin Multiplatform、JVM、Native、JS、Wasm 的更新和 Gradle、Maven 的构建工具支持。

发布时间:2026 年 3 月 16 日

有关缺陷修复版本 2.3.21 的详细信息,请参见变更日志

Kotlin 2.3.20 已经发布!以下是主要亮点:

提示: 有关 Kotlin 发布周期的信息,请参见 Kotlin 发布流程。

更新到 Kotlin 2.3.20

最新版本的 Kotlin 包含在最新版本的 IntelliJ IDEA 和 Android Studio 中。

要更新到新的 Kotlin 版本,请确保你的 IDE 已更新到最新版本,并在构建脚本中更改 Kotlin 版本为 2.3.20。

新特性

稳定

以下特性在此版本中已进入 Stable:

Kotlin 项目更简单的配置 {#%id1%}

Maven

Kotlin 2.3.20 让在 Maven 项目中配置 Kotlin 更容易。现在 Kotlin 支持自动配置源根和 Kotlin 标准库。

有了新配置,当你使用 Maven 构建系统创建新的 Kotlin 项目,或者把 Kotlin 引入现有的 Java Maven 项目时,都无需在 POM 构建文件中手动指定源根路径或添加 kotlin-stdlib 依赖。

如何启用 {#%id2%}

在你的 pom.xml 文件中,为 Kotlin Maven 插件的 <build><plugins> 部分添加 <extensions>true</extensions>:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<build>
    <plugins>
         <plugin>
             <groupId>org.jetbrains.kotlin</groupId>
             <artifactId>kotlin-maven-plugin</artifactId>
             <version>2.4.20</version>
             <extensions>true</extensions>
         </plugin>
    </plugins>
</build>

这里 <extensions> 选项的新特性包括:

  • 把 src/main/kotlin 和 src/test/kotlin 目录注册为源根(如果它们已存在但未在插件配置中指定)。
  • 添加 kotlin-stdlib 依赖(如果尚未显式定义)。

你也可以选择退出自动添加 Kotlin 标准库。为此,请在 <properties> 部分添加以下内容:

1
2
3
4
5
6
<project>
    <properties>

        <kotlin.smart.defaults.enabled>false</kotlin.smart.defaults.enabled>
    </properties>
</project>

请注意,该属性不仅会禁用自动添加标准库,还会禁用源根路径的注册。其他 <extensions> 特性不受影响。

有关配置 Kotlin Maven 项目的更多信息,请参见配置 Maven 项目。

新特性

实验性 - 实验

以下尚未稳定的特性在此版本中可用。这包括处于 Beta、Alpha 和实验性状态的特性:

Lombok 现在进入 Alpha {#%id3%}

Alpha 编译器

Kotlin 1.5.20 引入了实验性的 Lombok 编译器插件,让你可以在混合 Kotlin 和 Java 代码的模块中生成并使用 Java 的 Lombok 声明。

在 2.3.20 中,Lombok 编译器插件已升级为 Alpha,因为我们计划让该功能可用于生产,但它仍在开发中。

基于名称的解构 {#%id4%}

实验性 语言

Kotlin 2.3.20 引入了基于名称的解构声明,它把变量与属性名匹配,而不是依赖基于位置的 componentN() 函数。

以前,解构声明使用基于位置的解构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
data class User(val username: String, val email: String)

fun main() {
    val user = User("alice", "alice@example.com")

    val (email, username) = user

    println(email)
    // alice

    println(username)
    // alice@example.com
}

在这个示例中,由于解构依赖 componentN() 函数的顺序,email 接收到的是 username 的值,而 username 接收到的是 email 的值。

从 Kotlin 2.3.20 开始,你可以使用基于名称的解构,其中每个变量按名称引用属性:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fun main() {
    val user = User("alice", "alice@example.com")

    // 使用显式形式的基于名称解构
    (val mail = email, val name = username) = user

    println(name)
    // alice

    println(mail)
    // alice@example.com
}

基于名称的解构是实验性的。你可以通过 -Xname-based-destructuring 编译器选项控制编译器如何解释解构声明。

它有以下模式:

  • only-syntax 启用基于名称解构的显式形式,而不改变现有解构声明的行为。
  • name-mismatch 在数据类中使用基于位置的解构且变量名与属性名不匹配时报告警告。
  • complete 启用带圆括号的简短形式的基于名称解构,并继续支持带方括号语法的基于位置的解构。

如果你使用 complete 模式,带圆括号的简短解构语法会把变量与属性名匹配,而不是依赖位置:

1
val (email, username) = user

如何启用 {#%id5%}

要在项目中使用基于名称的解构,请把该编译器选项添加到构建配置文件中:

Gradle

1
2
3
4
5
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xname-based-destructuring=only-syntax")
    }
}

Maven

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<build>
    <plugins>
        <plugin>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-plugin</artifactId>
            <configuration>
                <args>
                    <arg>-Xname-based-destructuring=only-syntax</arg>
                </args>
            </configuration>
        </plugin>
    </plugins>
</build>

选择启用基于名称的解构还会引入一种使用方括号的基于位置解构的新语法:

1
2
// 使用显式的基于位置解构
val [username, email] = user

我们计划逐步转向默认使用基于名称匹配的解构声明,同时用新的方括号语法保留基于位置的解构。

更多信息请参见该特性的 KEEP。

我们欢迎你在 YouTrack 中提供反馈。

创建 Map.Entry 不可变副本的新 API {#%id6%}

实验性 标准库

Kotlin 2.3.20 引入了 Map.Entry.copy() 扩展函数,用于创建 Map.Entry 的不可变副本。该函数允许你在修改映射之后,先复制再从 Map.entries 获得的条目继续复用它们。

Map.Entry.copy() 是实验性的。要选择启用,请使用 @OptIn(ExperimentalStdlibApi::class) 注解或编译器选项:

1
-opt-in=kotlin.ExperimentalStdlibApi

以下是一个使用 Map.Entry.copy() 从可变映射中移除条目的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@OptIn(ExperimentalStdlibApi::class)
fun main() {
    val map = mutableMapOf(1 to 1, 2 to 2, 3 to 3, 4 to 4)

    val toRemove = map.entries
        .filter { it.key % 2 == 0 }
        .map { it.copy() }

    map.entries.removeAll(toRemove)

    println("map = $map")
    // map = {1=1, 3=3}
}

C 或 Objective-C 库的新互操作模式 {#%id7%}

实验性 Native

如果你在 Kotlin Multiplatform(KMP)库或应用中使用 C 或 Objective-C 库,我们邀请你测试新的互操作模式并分享结果。

总的来说,Kotlin/Native 支持把 C 和 Objective-C 库导入 Kotlin。然而对于 KMP 库,该功能目前受KMP 与旧编译器版本的兼容性问题影响。

换句话说,如果你发布用某个 Kotlin 版本编译的 KMP 库,导入 C 或 Objective-C 库可能会使该 Kotlin 库无法在更早 Kotlin 版本的项目中使用。

为了解决这个问题以及其他问题,Kotlin 团队一直在修订底层使用的互操作机制。从 Kotlin 2.3.20 开始,你可以通过一个编译器选项试用新模式。

如何启用 {#%id8%}

  1. 在你的 Gradle 构建文件中,检查是否有 cinterops {} 块或 pod() 依赖。如果存在,说明你的项目使用了 C 或 Objective-C 库。

  2. 确保你的项目使用 2.3.20 或更高版本。

  3. 在同一构建文件中,把 -Xccall-mode 编译器选项添加到 cinterop 工具的调用中:

1
2
3
4
5
6
7
8
9
   kotlin {
       targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>().configureEach {
           compilations.configureEach {
               cinterops.configureEach {
                   extraOpts += listOf("-Xccall-mode", "direct")
               }
           }
       }
   }
  1. 像往常一样构建并测试你的项目,例如运行单元测试、应用等。你也可以使用 --continue 选项让 Gradle 在失败后继续执行任务,从而一次性发现更多问题。

警告: 不要发布用新互操作模式编译的库,因为它仍然是实验性的。

报告你的结果 {#%id9%}

在大多数情况下,新的互操作模式应当可以无缝替换。我们计划最终将其默认启用。但要做到这一点,我们需要确保它尽可能好用,并在大量项目上进行测试,因为:

  • 新模式尚不支持某些 C 和 Objective-C 声明(主要是由于兼容性问题)。我们希望更好地了解这在现实世界中的影响,并据此安排后续步骤的优先级。
  • 可能存在我们尚未考虑到的缺陷或情况。测试具有大量相互交互特性的语言本身就很有挑战,而测试语言之间的交互(每种语言都有独特的一组特性)更是如此。

帮助我们检查真实项目并找出有挑战性的情况。无论你是否遇到问题,都请在 YouTrack 的评论中分享你的结果。

语言

Kotlin 2.3.20 添加了基于名称的解构声明,它把变量与属性名匹配,而不是依赖位置。它还引入了对带上下文参数声明的重载解析的变更。

带上下文参数的声明的重载解析变更

语言

Kotlin 2.3.20 引入了对带上下文参数声明的重载解析的变更。

以前,重载解析把带上下文参数的声明视为比不带上下文参数的声明更具体。

从 Kotlin 2.3.20 开始,该规则不再适用,使重载选择更加统一。结果,当重载仅在上下文参数上不同时,此前能解析的调用现在变得有歧义,从而导致编译错误。在这类情况下,编译器会警告可能存在歧义。

以下是一个示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Logger {
    fun info(msg: String) = println("INFO: $msg")
}

fun saveUser(id: Int) {
    println("Saving user $id (no logger)")
}

// 警告:上下文声明被遮蔽
context(logger: Logger)
fun saveUser(id: Int) {
    logger.info("Saving user $id")
}

fun main() {
    val logger = Logger()

    context(logger) {
        // 在 2.3.20 中报告歧义错误
        saveUser(1)
    }
}

此外,Kotlin 2.3.20 把 kotlin.context 的重载数量从 22 个减少到 6 个,以减少解析和代码补全期间过多的重载候选。

标准库

Kotlin 2.3.20 为标准库引入了一个新的实验性特性。

Kotlin 编译器插件

Kotlin 2.3.20 为 Lombok 和 kotlin.plugin.jpa 编译器插件带来了重要更新。

kotlin.plugin.jpa 插件中改进的 JPA 支持

编译器

kotlin.plugin.jpa 插件现在除了应用现有的 no-arg 编译器插件之外,还会自动应用带新增内置 JPA 预设的 all-open 编译器插件。

以前,使用 kotlin("plugin.jpa") 只会启用带 JPA 预设的 no-arg 插件。

在此版本中,我们改进了 kotlin.plugin.jpa 预设,使其自动配置 all-open 插件。这确保了懒加载关联按预期工作,而不是导致预加载并触发额外查询。

从 Kotlin 2.3.20 开始:

  • all-open 编译器插件提供 JPA 预设。
  • Gradle 的 org.jetbrains.kotlin.plugin.jpa 插件会自动应用启用了 JPA 预设的 org.jetbrains.kotlin.plugin.all-open 插件。
  • Maven JPA 配置默认启用带 JPA 预设的 all-open。(IntelliJ IDEA 中的支持从 2026.1 起可用。)
  • Maven 依赖 org.jetbrains.kotlin:kotlin-maven-noarg 现在隐式包含 org.jetbrains.kotlin:kotlin-maven-allopen,因此你不再需要在 <plugin><dependencies> 块中显式添加它。

因此,使用以下注解标记的 JPA 实体会被自动视为 open,并在无需额外配置的情况下获得无参构造器:

  • javax.persistence.Entity
  • javax.persistence.Embeddable
  • javax.persistence.MappedSuperclass
  • jakarta.persistence.Entity
  • jakarta.persistence.Embeddable
  • jakarta.persistence.MappedSuperclass

这一变更简化了构建配置,并改善了在 Kotlin 中使用 JPA 框架的开箱即用体验。

提示: 即将发布的 IntelliJ IDEA 2026.1 会在项目中配置 Kotlin 时自动配置 kotlin.plugin.jpa 插件。IDE 会提供快速修复来添加该插件并移除任何冗余的无参构造器声明。

Kotlin/JVM

Kotlin 2.3.20 为 Java 互操作引入了若干改进。编译器现在能识别 Vert.x 的 @Nullable 注解以进行可空性检查。此版本还添加了对 Java @Unmodifiable 和 @UnmodifiableView 注解的支持,以便在 Kotlin 中把带注解的集合视为只读。

支持 Vert.x @Nullable 注解

Jvm

Kotlin 2.3.20 添加了对 io.vertx.codegen.annotations.Nullable 注解的支持。编译器现在能识别该注解,并默认把可空性不匹配报告为警告。

要强制进行严格的可空性检查并把这些警告升级为错误,请在构建文件中添加以下编译器选项:

Gradle

1
2
3
4
5
6
// build.gradle(.kts)
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xnullability-annotations=@io.vertx.codegen.annotations:strict")
    }
}

Maven

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

<build>
    <plugins>
        <plugin>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-plugin</artifactId>
            <configuration>
                <args>
                    <arg>-Xnullability-annotations=@io.vertx.codegen.annotations:strict</arg>
                </args>
            </configuration>
        </plugin>
    </plugins>
</build>

支持 Java 不可修改集合注解

Jvm

Kotlin 2.3.20 添加了对 org.jetbrains.annotations.Unmodifiable 和 org.jetbrains.annotations.UnmodifiableView Java 注解的支持。

从 Kotlin 2.3.20 开始,用这些注解标记的 Java 声明所返回的集合在 Kotlin 中会被视为只读。把它们赋给可变集合类型会产生类型不匹配警告。该警告计划在 Kotlin 2.5.0 中变为错误。

以下是一个示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Java
public class Java {
    public static @UnmodifiableView List<Object> unmodifiableView() {
        return List.of();
    }

    public static @Unmodifiable List<Object> unmodifiable() {
        return List.of();
    }
}
1
2
3
4
5
6
7
// Kotlin

fun main() {
    // 警告:Java 类型不匹配
    val mutableView: MutableList<Any> = Java.unmodifiableView()
    val mutableCopy: MutableList<Any> = Java.unmodifiable()
}

Kotlin/Native

Kotlin 2.3.20 引入了用于 C 和 Objective-C 库的新实验性互操作模式、交叉编译检查器,以及用于在 Kotlin/Native 项目中禁用编译缓存的新 DSL。

交叉编译检查器

Native

Kotlin 2.3.20 引入了一种判断给定目标是否支持交叉编译的方式。这对跟踪编译任务状态的第三方插件很有用。

总的来说,Kotlin/Native 允许交叉编译,这意味着任何受支持的主机都可以为受支持的目标产出 .klib 制品。然而,如果你的项目使用 cinterop 依赖,Apple 目标的制品产出仍然受限。

新的 crossCompilationSupported API 现在会检查是否支持交叉编译:该目标应由主机管理器启用,并且该目标的任何编译都不涉及 cinterop 依赖。该检查器默认启用。

有关受支持目标和主机的更多信息,请参见 Kotlin/Native 文档。

用于禁用编译缓存的新 DSL

Native

Kotlin 2.3.20 带来了用于在 Kotlin/Native 项目中禁用编译缓存的新 DSL。它旨在让禁用缓存的决定更加审慎和明确。

由于禁用缓存会显著拖慢 Kotlin/Native 构建,因此只应临时并作为例外使用。所以,禁用缓存现在与特定的 Kotlin 版本绑定,并且必须包含原因,起到文档的作用。

如果你确实需要在项目中禁用编译缓存,请按如下方式更新 Gradle 构建文件中的 binaries {} 块:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
kotlin {
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        // 指定你的二进制类型
        it.binaries.framework {
            baseName = "CacheKind"
            isStatic = true

            // 用新 DSL 禁用缓存
            disableNativeCache(
                 version = DisableCacheInKotlinVersion.2_3_0,
                 reason = "Cache bug",
                 issue = URI("https://youtrack.com/YY-1111")
            )
        }
    }
}
  • version——禁用编译缓存所针对的 Kotlin 版本。
  • reason(必填)——禁用编译缓存的原因。
  • issue(可选)——你的缺陷跟踪器中对应议题的 URL。

新 DSL 取代了已弃用的 kotlin.native.cacheKind Gradle 属性。你可以放心地从 gradle.properties 文件中移除它。

关于改善编译时间的更多提示,请参见 Kotlin/Native 文档。

Kotlin/Wasm

Kotlin 2.3.20 改进了字符串操作的性能、编译时间和内存使用。它还添加了对实验性 @nativeInvoke 注解的支持,让你可以像调用 JavaScript 函数那样调用 Kotlin 对象或类。

改进的字符串性能

Wasm

Kotlin/Wasm 现在使用 JS String 内置对象来对 kotlin.String 值进行操作。这让 Kotlin/Wasm 能在支持该提案的浏览器和 Wasm 运行时中受益于 JavaScript 引擎的字符串优化。该优化适用于拼接、字符串插值、StringBuilder.append() 和数字到字符串转换等操作。

它带来了:

  • 在针对性基准测试中,字符串插值最快提升 4.6 倍。
  • 在 KotlinConf 应用构建中,Wasm 二进制体积约缩小 5%。
  • 在所有 Wasm 基准测试中位数约提升 1%。
  • 在大量追加操作的负载中,StringBuilder.append() 和 kotlin.String 实例拼接至少快 20%。

改进的编译时间与内存优化

Wasm

Kotlin 2.3.20 添加了编译器优化,显著减少了编译期间的内存消耗,尤其是在大型项目中。这些优化还改善了增量构建性能。

在我们的测试中,我们观察到干净构建时间提升了 65%,增量构建时间提升了 21%。

支持 @nativeInvoke 注解

实验性 Wasm

Kotlin 2.3.20 为 wasmJs 目标引入了对 @nativeInvoke 注解的支持。该注解允许你把 Kotlin 对象或类视为 JavaScript 中的函数。它的设计目的是把 external 声明(类或接口)的成员函数标记为 JavaScript 对象的“调用运算符”。

当你注解某个函数时,Kotlin 中对它的每次调用都会被翻译为对 JavaScript 对象本身的直接调用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import kotlin.js.nativeInvoke

@OptIn(ExperimentalWasmJsInterop::class)
external class JsAction {
    @nativeInvoke
    operator fun invoke(data: String)
}

fun main() {
    val action = JsAction()
    action("Run task")
}

这是在设计出 Kotlin/Wasm 与 JavaScript 之间的稳定互操作之前的临时方案。它可能在未来的版本中被修改或移除,使用它时编译器会报告警告。

有关 Kotlin/Wasm 与 JavaScript 互操作的更多信息,请参见与 JavaScript 的互操作。

Kotlin/JS

Kotlin 2.3.20 使从 TypeScript 实现 Kotlin 接口成为可能,并引入了对 SWC 编译平台的实验性支持。

从 JavaScript/TypeScript 实现 Kotlin 接口

实验性 Js

Kotlin 2.3.20 取消了在 JavaScript/TypeScript 一侧实现 Kotlin 接口的限制。以前只能把 Kotlin 接口作为 TypeScript 接口导出到 TypeScript,禁止从 TypeScript 实现它们。

现在你可以按以下方式实现任何 Kotlin 接口:

1
2
3
4
5
6
7
8
// Kotlin
@JsExport
interface DataProcessor {
    suspend fun process(): String
}

@JsExport
fun registerProcessor(processor: DataProcessor) { ... }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// TypeScript
import { DataProcessor, registerProcessor } from "my-kmp-library"

class JsonProcessor implements DataProcessor {
    readonly [DataProcessor.Symbol] = true

    async process(): Promise<string> {
        return "processed JSON data"
    }
}

registerProcessor(new JsonProcessor())

也可以从 TypeScript 复用 Kotlin 的默认实现。尽管 TypeScript 没有接口默认实现的概念,你可以通过委托给 DefaultImpls 对象来绕过这一限制:

1
2
3
4
5
6
// Kotlin
@JsExport
interface Logger {
    fun log(): String = "[INFO] Default log entry"
    val prefix: String get() = "LOG"
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// TypeScript
import { Logger, acceptLogger } from "my-kmp-library"

class ConsoleLogger implements Logger {
    readonly [Logger.Symbol] = true

    // 委托给默认方法实现
    log(): string {
        return Logger.DefaultImpls.log(this);
    }

    // 委托给默认属性实现
    get prefix(): string {
        return Logger.DefaultImpls.prefix.get(this);
    }
}

acceptLogger(new ConsoleLogger())

如何启用

在你的构建文件中,添加新的编译器选项:

1
2
3
4
5
6
7
8
9
kotlin {
    js {
        // ...
        generateTypeScriptDefinitions()
        compilerOptions {
            freeCompilerArgs.add("-Xenable-implementing-interfaces-from-typescript")
        }
    }
}

更多信息请参见 @JsExport 注解。

支持 SWC 编译平台

实验性 Js

从 Kotlin 2.3.20 开始,Kotlin/JS 支持 SWC 编译平台。它有助于把较新版本的 JavaScript/TypeScript 代码转译为更旧、兼容性更好的 JavaScript 代码。

把代码转换交给外部工具,使我们能够减少 Kotlin/JS 编译器产出的变体数量,并加快编译器的现代化进程,只需专注于支持最新的 JavaScript 特性。目前,受支持的最新 ECMAScript 版本仍是 es2015。

此外,把转译工作交出去还让我们能够改进内联 JavaScript 特性。目前它只支持 ES5 语法(这将在 2.4.0 中改变)。在面向更低版本的同时支持更新的语法会很困难,因为这要求编译器转译内联 JS 块内部的 JS 代码。有了 SWC,我们将能够添加现代 JS 语法,该工具会把代码转译为终端用户版本所需的语法。

迁移到 SWC 还让你有机会在 Kotlin Gradle 插件内实现基于 browserlist 的 DSL。这让你可以声明目标浏览器或环境,而不是具体的 JS 版本。

如何启用

在你的 gradle.properties 文件中,添加以下选项:

1
kotlin.js.delegated.transpilation=true

我们计划在未来的 Kotlin 版本中稳定通过 SWC 进行的转译。在它成为默认方式之后,编译多个 JS 目标的功能将完全从 Kotlin/JS 编译器委托给转译器。

有关 SWC 平台的更多信息,请参见官方文档。

Gradle

Kotlin 2.3.20 与新版 Gradle 兼容,并包含对 Kotlin Gradle 插件中 Kotlin/JVM 编译的变更。

兼容 Gradle 9.3.0

Gradle

Kotlin 2.3.20 与 Gradle 7.6.3 到 9.3.0 完全兼容。你也可以使用最新 Gradle 版本以内的其他版本。不过请注意,这样做可能会产生弃用警告,并且某些新的 Gradle 特性可能无法工作。

KGP 中二进制兼容性验证的改进

Gradle

Kotlin 2.2.0 首次带来了 Kotlin Gradle 插件中的二进制兼容性验证支持。Kotlin 2.3.20 添加了两项改进。

首先,二进制兼容性验证 Gradle 任务的名称中不再包含 “Legacy”。我们做这一改动是因为旧的命名约定让 Kotlin 开发者感到困惑:

| 旧名称 | 新名称 |

| checkLegacyAbi | checkKotlinAbi | | updateLegacyAbi | updateKotlinAbi | | dumpLegacyAbi | internalDumpKotlinAbi |

旧的任务名称在 Kotlin 2.3.20 中仍然存在,以便向新名称过渡。

其次,如果你在项目中启用了二进制兼容性验证,现在当你运行 check 任务时,Gradle 会自动运行 checkKotlinAbi 任务。以前,即使 check 任务本应运行所有验证任务,Gradle 也不会运行 checkKotlinAbi 任务。这导致 Gradle 项目中的行为不一致。

Kotlin/JVM 编译默认使用 Build tools API

实验性 - 通用 Gradle

在 Kotlin 2.3.20 中,Kotlin Gradle 插件中的 Kotlin/JVM 编译默认使用 Build tools API(BTA)。这一内部编译基础设施的变更使 Kotlin 编译器的构建工具支持能够更快地发展。

如果你发现任何问题,请在我们的问题跟踪器中分享你的反馈。

Maven

Kotlin 2.3.20 带来了一个重要的变更,使 Maven 项目的配置更容易。

Build tools API

Kotlin 2.3.20 为希望使用 Build tools API(BTA)把自己的构建系统与 Kotlin 编译器集成的开发者引入了更多变更。

构建操作的改进

Bta

在此版本中,BTA 改进了构建工具管理构建操作的方式。构建操作让构建工具可以与 Kotlin 编译器交互。每个构建操作都是 BuildOperation 接口的一个实现。

你现在可以用 cancel() 函数取消实现了 CancellableBuildOperation 接口的构建操作。

cancel() 函数以“尽力而为”的方式工作。这意味着不保证该操作一定被取消。

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
val operation = toolchains.jvm.jvmCompilationOperationBuilder(sources, destination) {}

toolchains.createBuildSession().use {
    try {
        it.executeOperation(operation.build())
    } catch (e: OperationCancelledException) {
        println("Build operation has been cancelled.")
    }
}

// ...

// 从另一个线程:
operation.cancel()

此外,构建操作现在更加健壮,因为你可以把它们创建为在启动后不可更改。为此,构建工具必须使用构建器模式:

  1. 使用可变构建器配置对象。
  2. 调用 build() 函数创建该对象的不可变实例。

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
fun prepareBuildOperation(toolchains: KotlinToolchains, sources: List<Path>, destination: Path): JvmCompilationOperation {
    val builder = toolchains.jvm.jvmCompilationOperationBuilder(sources, destination)

    // 使用构建器配置该操作
    builder.compilerArguments[CommonToolArguments.VERBOSE] = true
    builder[COMPILER_ARGUMENTS_LOG_LEVEL] = CompilerArgumentsLogLevel.ERROR

    // 返回不可变操作
    return builder.build()
}

跨构建工具的一致指标收集

Bta

在 Kotlin 2.3.20 之前,构建指标基础设施以 Gradle 为中心,这影响了基础设施的某些部分,例如指标名称。此外,并非所有指标都适用于不同的编译器执行策略。

在 Kotlin 2.3.20 中,BTA 为 JVM 提供了与构建工具无关的指标收集。BTA 还引入了一组一致的指标,无论编译器执行策略如何。特定于某种编译方式或编译器执行策略的指标只在适用时报告。例如,增量编译指标只对增量构建可用,守护进程特有的指标只在使用 Kotlin 守护进程时可用。

构建工具现在可以为构建操作配置 BuildMetricsCollector 对象,以捕获让用户了解构建性能的构建指标:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
val operation =
    kotlinToolchains.jvm.jvmCompilationOperationBuilder(sources, outputDirectory)
operation[BuildOperation.METRICS_COLLECTOR] = object : BuildMetricsCollector {
    override fun collectMetric(
        name: String,
        type: BuildMetricsCollector.ValueType,
        value: Long
    ) {
        // ...
    }
}

构建工具更轻松地配置编译器插件

Bta

在 Kotlin 2.3.20 中,BTA 为构建工具配置编译器插件提供了一种新的、更简单的方式。这种方式允许构建工具把配置直接传播给它们的用户。

构建工具不再通过命令行用实验性编译器选项配置编译器插件,而可以使用 kotlin.buildtools.api.arguments.CommonCompilerArguments.COMPILER_PLUGINS 选项来配置一组表示编译器插件配置的对象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import org.jetbrains.kotlin.buildtools.api.KotlinToolchains
import org.jetbrains.kotlin.buildtools.api.arguments.CompilerPlugin
import org.jetbrains.kotlin.buildtools.api.arguments.CompilerPluginOption
import org.jetbrains.kotlin.buildtools.api.arguments.CommonCompilerArguments.Companion.COMPILER_PLUGINS
import org.jetbrains.kotlin.buildtools.api.arguments.CompilerPlugin
import org.jetbrains.kotlin.buildtools.api.arguments.CompilerPluginOption
import org.jetbrains.kotlin.buildtools.api.jvm.JvmPlatformToolchain
import org.jetbrains.kotlin.buildtools.api.jvm.JvmPlatformToolchain.Companion.jvm
import org.jetbrains.kotlin.buildtools.api.jvm.operations.JvmCompilationOperation
import java.nio.file.Path

...

val toolchains: KotlinToolchains = ...
val jvmToolchain: JvmPlatformToolchain = toolchains.jvm
val operation: JvmCompilationOperation.Builder = jvmToolchain.jvmCompilationOperationBuilder(...)
val noArgPluginClasspath: List<Path> = ...
operation.compilerArguments[COMPILER_PLUGINS] = listOf(
    CompilerPlugin(
        pluginId = "org.jetbrains.kotlin.noarg",
        classpath = noArgPluginClasspath,
        rawArguments = listOf(CompilerPluginOption("annotation", "GenerateNoArgsConstructor")),
        orderingRequirements = emptySet(),
    )
)

示例代码

破坏性变更与弃用

本节重点介绍重要的破坏性变更和弃用。有关 Kotlin 2.3.0 和 2.3.20 中弃用的更多信息,请参见兼容性指南。

  • 在 Kotlin 2.3.20 中,Kotlin/Wasm 把模块初始化作为 Wasm 模块实例化的一部分执行,而不再依赖外部 JavaScript 随后调用 _initialize() 函数。这一变更让 Kotlin/Wasm 更加独立,并为 ES 模块集成提案做好准备。

如果你使用 @EagerInitialization 注解,相关代码如果在模块初始化完成之前运行,可能会失败。除非确实需要,我们建议避免使用 @EagerInitialization 注解。

  • 实验性的上下文接收者已不再受支持,并由上下文参数取代。
  • 此版本推进了 Intel 芯片 Apple 目标的弃用周期。从 Kotlin 2.3.20 开始,我们弃用 macosX64、tvosX64 和 watchosX64 目标。我们计划在下一个 Kotlin 版本中完全移除对这些目标的支持。

由于许多第三方库仍依赖 iosX64 目标,我们暂时把它保留在支持层级 3。这意味着我们不保证 CI 测试,也可能不提供不同编译器版本之间的源码和二进制兼容性。有关支持层级的更多信息,请参见 Kotlin/Native 目标支持。

  • 在 Kotlin 2.3.20 中,Kotlin Multiplatform 中更严格的依赖匹配可能在通用源集与平台源集的依赖解析不一致时导致元数据编译失败。详情和变通方案请参见 YouTrack 中的议题。

文档更新

我们在 Kotlin 生态中做了以下文档变更:

  • Kotlin 路线图——查看 Kotlin 在语言和生态演进方面优先事项的更新列表。
  • 升级到 AGP 9——了解我们关于把带 Android 应用的多平台项目迁移到 AGP 9 的建议。
  • 为 KMP 应用配置 CI——按照教程为多平台项目配置 GitHub Actions 持续集成。
  • Compose UI 预览——了解如何在 IDE 中预览 composable 而无需运行模拟器。
  • 处理 Web 资源——查找关于如何在 Compose Multiplatform 中处理 Web 资源的信息。
  • 配置视口——了解如何使用 ComposeViewport() 函数在用于 Web 的 Compose Multiplatform 中把 UI 渲染到 HTML canvas 上。
  • 自定义编译器插件——了解编译器插件如何工作,以及当你找不到适合自己用例的插件时可以做什么。
  • 应用结构——为你的 Ktor Server 应用选择最佳应用结构。
  • HTTP 请求生命周期——了解如何使用 HTTP 请求生命周期在客户端断开时取消 Ktor 中的请求处理。
  • 依赖注入——了解如何在 Ktor Server 中配置依赖注入,包含更新的指导和实用示例。
  • Exposed 的 Spring Boot 集成——了解如何在 Spring Boot 3 和 4 中使用 Exposed。