13.6.1.2 Kotlin 2.3.0 新变化

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

13.6.1.2 Kotlin 2.3.0 新变化

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

发布时间:2025 年 12 月 16 日

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

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

你也可以在这个视频中查看这些更新的概述:

视频:Hands-on with Kotlin 2.3

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

IDE 支持

支持 2.3.0 的 Kotlin 插件已捆绑在最新版本的 IntelliJ IDEA 和 Android Studio 中。你无需在 IDE 中更新 Kotlin 插件。你需要做的只是在构建脚本中更改 Kotlin 版本为 2.3.0。

详情请参见更新到新版本。

语言

Kotlin 2.3.0 专注于特性稳定化,引入了检测未使用返回值的新机制,并改进了上下文敏感解析。

稳定特性

在此前的 Kotlin 版本中,一些新的语言特性作为实验性或 Beta 特性引入。以下特性现在已在 Kotlin 2.3.0 中升级为 Stable:

默认启用的特性

在 Kotlin 2.3.0 中,在带显式返回类型的表达式体中使用 return 语句现已默认启用。

查看 Kotlin 语言特性与提案的完整列表。

未使用返回值检查器

实验性 - 通用

Kotlin 2.3.0 引入了未使用返回值检查器,以帮助防止结果被忽略。只要某个表达式返回的值不是 Unit 或 Nothing,且既没有传给函数、也没有用在条件中或以其他方式使用,它就会发出警告。

该检查器有助于捕捉函数调用产生了有意义的结果却被静默丢弃的缺陷,这类问题可能导致意外行为或难以追踪的问题。

注意: 该检查器会忽略 ++ 和 -- 之类自增操作返回的值。

考虑以下示例:

1
2
3
4
5
6
7
8
9
fun formatGreeting(name: String): String {
    if (name.isBlank()) return "Hello, anonymous user!"
    if (!name.contains(' ')) {
        // 检查器会警告此结果被忽略
        "Hello, " + name.replaceFirstChar(Char::titlecase) + "!"
    }
    val (first, last) = name.split(' ')
    return "Hello, $first! Or should I call you Dr. $last?"
}

在这个示例中,创建了一个字符串但从未使用,因此检查器会把它报告为被忽略的结果。

该特性是实验性的。要选择启用,请在构建文件中添加以下编译器选项:

Gradle

1
2
3
4
5
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xreturn-value-checker=check")
    }
}

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>-Xreturn-value-checker=check</arg>
                </args>
            </configuration>
        </plugin>
    </plugins>
</build>

使用该选项时,检查器只会报告来自已标记表达式的被忽略结果,例如 Kotlin 标准库中的大多数函数。

要标记你自己的函数,请使用 @MustUseReturnValues 注解标记你希望检查器报告被忽略返回值的范围。

例如,你可以标记整个文件:

1
2
3
4
5
6
// 标记此文件中的所有函数和类,让检查器报告未使用的返回值
@file:MustUseReturnValues

package my.project

fun someFunction(): String

或者,你也可以标记特定的类:

1
2
3
4
5
6
7
// 标记此类中的所有函数,让检查器报告未使用的返回值
@MustUseReturnValues
class Greeter {
    fun greet(name: String): String = "Hello, $name"
}

fun someFunction(): Int = ...

你也可以通过在构建文件中添加以下编译器选项来标记整个项目:

Gradle

1
2
3
4
5
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xreturn-value-checker=full")
    }
}

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>-Xreturn-value-checker=full</arg>
                </args>
            </configuration>
        </plugin>
    </plugins>
</build>

使用该设置时,Kotlin 会自动把你的编译文件视为已使用 @MustUseReturnValues 注解标记,检查器会报告项目函数的所有返回值。

你可以用 @IgnorableReturnValue 注解标记特定函数来抑制警告。对忽略返回值很常见且符合预期的函数使用该注解,例如 MutableList.add:

1
2
3
4
@IgnorableReturnValue
fun <T> MutableList<T>.addAndIgnoreResult(element: T): Boolean {
    return add(element)
}

你可以在不把函数本身标记为可忽略的情况下抑制警告。为此,请把结果赋给一个带下划线(_)的特殊未命名变量:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 不可忽略的函数
fun computeValue(): Int = 42

fun main() {
    // 报告警告:结果被忽略
    computeValue()

    // 用特殊的未使用变量仅在此调用处抑制警告
    val _ = computeValue()
}

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

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

显式后备字段

实验性

Kotlin 2.3.0 引入了显式后备字段——一种用于显式声明保存属性值的底层字段的新语法,与现有的隐式后备字段形成对比。

你可以在这个视频中查看该特性的概述:

视频:Explicit Backing Fields are experimental in Kotlin 2.3

新显式语法简化了常见的后备属性模式,即属性的内部类型与其对外 API 类型不同。例如,你可能内部使用 ArrayList,而对外暴露为只读的 List 或 MutableList。以前这需要额外的私有属性。

有了显式后备字段,field 的实现类型直接在属性作用域内定义。这消除了对单独私有属性的需要,并允许编译器在同一私有作用域内自动对后备字段的类型执行智能转换。

之前:

1
2
3
4
5
6
private val _city = MutableStateFlow<String>("")
val city: StateFlow<String> get() = _city

fun updateCity(newCity: String) {
    _city.value = newCity
}

之后:

1
2
3
4
5
6
7
val city: StateFlow<String>
    field = MutableStateFlow("")

fun updateCity(newCity: String) {
    // 智能转换会自动生效
    city.value = newCity
}

该特性是实验性的。要选择启用,请在构建文件中添加以下编译器选项:

Gradle

1
2
3
4
5
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xexplicit-backing-fields")
    }
}

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>-Xexplicit-backing-fields</arg>
                </args>
            </configuration>
        </plugin>
    </plugins>
</build>

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

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

上下文敏感解析的变更

实验性 - 通用

上下文敏感解析仍然是实验性的,但我们正根据用户反馈不断改进该特性:

  • 当前类型的密封父类型和外围父类型现在被视为搜索的上下文作用域的一部分。不再考虑其他父类型作用域。有关动机和示例,请参见 KT-77823 YouTrack 议题。
  • 当涉及类型运算符和相等性时,如果使用上下文敏感解析导致解析产生歧义,编译器现在会报告警告。例如,当导入了冲突的类声明时就可能发生这种情况。有关动机和示例,请参见 KT-77821 YouTrack 议题。

请在 KEEP 中查看当前提案的全文。

Kotlin/JVM:支持 Java 25

从 Kotlin 2.3.0 开始,编译器可以生成包含 Java 25 字节码的类。

Kotlin/Native

Kotlin 2.3.0 改进了 Swift 导出的支持和 C 与 Objective-C 库的导入,并提升了 release 任务的构建时间。

通过 Swift 导出改进互操作

实验性 - 通用

Kotlin 2.3.0 通过 Swift 导出进一步改进了 Kotlin 与 Swift 的互操作,添加了对原生 enum 类和可变参数函数的支持。

以前,Kotlin 枚举被导出为普通的 Swift 类。现在映射是直接的,你可以使用常规的原生 Swift 枚举。例如:

1
2
3
4
5
6
7
8
// Kotlin
enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF)
}

val color = Color.RED
1
2
3
4
5
6
// Swift
public enum Color: Swift.CaseIterable, Swift.LosslessStringConvertible, Swift.RawRepresentable {
    case RED, GREEN, BLUE

    var rgb: Int { get }
}

此外,Kotlin 的 vararg 函数现在会直接映射为 Swift 的可变参数函数参数。

这类函数让你可以传入可变数量的参数。当你事先不知道参数数量,或者想在不指定类型的情况下创建或传入集合时,这会很有用。例如:

1
2
// Kotlin
fun log(vararg messages: String)
1
2
// Swift
public func log(messages: Swift.String...)

注意: 尚不支持可变参数函数参数中的泛型类型。

C 和 Objective-C 库导入进入 Beta

Beta

把 C 和 Objective-C 库导入 Kotlin/Native 项目的支持处于 Beta 阶段。

与不同版本的 Kotlin、依赖项和 Xcode 的完全兼容性仍未得到保证,但编译器现在会在出现二进制兼容性问题时提供更好的诊断信息。

该导入尚未稳定,在你的项目中使用 C 和 Objective-C 库时,对于与 C 和 Objective-C 互操作相关的某些内容,仍然需要使用 @ExperimentalForeignApi 选择启用注解,包括:

  • kotlinx.cinterop.* 包中的一些 API,在处理原生库或内存时需要用到。
  • 原生库中的所有声明,平台库除外。

为了兼容性并避免你不得不更改源代码,新的稳定状态没有体现在注解名称中。

更多信息请参见 C 和 Objective-C 库导入的稳定性。

Objective-C 头文件块类型中的默认显式名称

在 Kotlin 2.2.20 中引入的 Kotlin 函数类型中的显式参数名,现在从 Kotlin/Native 项目导出的 Objective-C 头文件中默认启用。这些参数名改进了 Xcode 中的自动补全建议,并有助于避免 Clang 警告。

考虑以下 Kotlin 代码:

1
2
// Kotlin:
fun greetUser(block: (name: String) -> Unit) = block("John")

Kotlin 会把 Kotlin 函数类型中的参数名转发到 Objective-C 块类型,让 Xcode 可以在建议中使用它们:

1
2
3
4
// Objective-C:
greetUserBlock:^(NSString *name) {
    // ...
};

如果你遇到问题,可以禁用显式参数名。为此,请在 gradle.properties 文件中添加以下二进制选项:

kotlin.native.binary.objcExportBlockExplicitParameterNames=false

请把任何问题报告到 YouTrack。

release 任务的构建时间更快

Kotlin/Native 在 2.3.0 中获得了若干性能改进。它们使 linkRelease* 之类的 release 任务(例如 linkReleaseFrameworkIosArm64)构建得更快。

根据我们的基准测试,release 构建最多可加快 40%,具体取决于项目规模。这些改进在面向 iOS 的 Kotlin Multiplatform 项目中最明显。

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

Apple 目标支持的变更

Kotlin 2.3.0 提高了 Apple 目标的最低支持版本:

  • 对于 iOS 和 tvOS,从 12.0 提高到 14.0。
  • 对于 watchOS,从 5.0 提高到 7.0。

根据公开数据,旧版本的使用已经非常有限。这一变更简化了我们对 Apple 目标的整体维护,并为在 Kotlin/Native 中支持 Mac Catalyst 创造了机会。

如果你必须在项目中保留较旧的版本,请在构建文件中添加以下代码行:

1
2
3
4
5
6
7
8
kotlin {
    targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>().configureEach {
        binaries.configureEach {
            freeCompilerArgs += "-Xoverride-konan-properties=minVersion.ios=12.0"
            freeCompilerArgs += "-Xoverride-konan-properties=minVersion.tvos=12.0"
        }
    }
}

请注意,这样的配置不保证能够成功编译,并且可能在构建期间或运行时破坏你的应用。

此版本还推进了 Intel 芯片 Apple 目标的弃用周期。

从 Kotlin 2.3.0 开始,macosX64、iosX64、tvosX64 和 watchosX64 目标被降级为支持层级 3。这意味着它们不保证在 CI 上测试,不同编译器版本之间的源码和二进制兼容性也可能无法保证。我们计划最终在 Kotlin 2.4.0 中移除对 x86_64 Apple 目标的支持。

更多信息请参见 Kotlin/Native 目标支持。

Kotlin/Wasm

Kotlin 2.3.0 为 Kotlin/Wasm 目标默认启用完全限定名称,为 wasmWasi 目标默认启用新的异常处理提案,并引入了 Latin-1 字符的紧凑存储。

默认启用完全限定名称

在 Kotlin/Wasm 目标上,完全限定名称(FQN)此前在运行时并未默认启用。你必须手动启用对 KClass.qualifiedName 属性的支持才能使用 FQN。

此前只能访问类名(不含包名),这给从 JVM 移植到 Wasm 目标的代码,或者期望在运行时获得完全限定名称的库带来了问题。

在 Kotlin 2.3.0 中,KClass.qualifiedName 属性在 Kotlin/Wasm 目标上默认启用。这意味着无需任何额外配置即可在运行时获得 FQN。

默认启用 FQN 提升了代码可移植性,并通过显示完全限定名称让运行时错误信息更有用。

得益于编译器优化——使用紧凑存储保存 Latin-1 字符串字面量以减少元数据,这一变更不会增加编译后的 Wasm 二进制体积。

Latin-1 字符的紧凑存储

以前,Kotlin/Wasm 按原样存储字符串字面量数据,意味着每个字符都以 UTF-16 编码。对于只包含或主要包含 Latin-1 字符的文本,这并不是最优的做法。

从 Kotlin 2.3.0 开始,Kotlin/Wasm 编译器会把只包含 Latin-1 字符的字符串字面量以 UTF-8 格式存储。

正如在 JetBrains 的 KotlinConf 应用上所做的实验所示,这一优化显著减少了元数据。它带来了:

  • 与未做该优化的构建相比,Wasm 二进制体积最多缩小 13%。
  • 即使在启用完全限定名称的情况下,与不存储它们的早期版本相比,Wasm 二进制体积也最多缩小 8%。

这种紧凑存储对下载和启动时间很重要的 Web 环境尤为重要。此外,这一优化消除了此前阻碍存储类和完全限定名称并默认启用 KClass.qualifiedName的体积障碍。

此变更默认启用,无需进一步操作。

wasmWasi 默认启用新的异常处理提案

以前,Kotlin/Wasm 对所有目标(包括 wasmWasi)都使用旧版异常处理提案。然而,大多数独立的 WebAssembly 虚拟机(VM)正在与新版异常处理提案保持一致。

从 Kotlin 2.3.0 开始,新的 WebAssembly 异常处理提案在 wasmWasi 目标上默认启用,确保与现代 WebAssembly 运行时更好地兼容。

对于 wasmWasi 目标,提前引入该变更是安全的,因为面向它的应用通常运行在差异较小的运行时环境中(常常运行在某个特定 VM 上),且通常由用户控制,从而降低了兼容性问题的风险。

新的异常处理提案对 wasmJs 目标仍默认关闭。你可以使用 -Xwasm-use-new-exception-proposal 编译器选项手动启用它。

Kotlin/JS

Kotlin 2.3.0 带来了把挂起函数导出到 JavaScript 的实验性支持,以及用 BigInt64Array 类型表示 Kotlin 的 LongArray 类型。

在此版本中,你现在可以用统一的方式访问接口内的伴生对象,在带伴生对象的接口中使用 @JsStatic 注解,在单个函数和类中使用 @JsQualifier 注解,并通过新注解 @JsExport.Default 使用默认导出。

使用 JsExport 导出挂起函数的新能力

实验性

以前,@JsExport 注解不允许把挂起函数(或包含这类函数的类和接口)导出到 JavaScript。你必须手动包装每个挂起函数,这既繁琐又容易出错。

从 Kotlin 2.3.0 开始,可以使用 @JsExport 注解把挂起函数直接导出到 JavaScript。

启用挂起函数导出减少了样板代码,并改善了 Kotlin/JS 与 JavaScript/TypeScript(JS/TS)之间的互操作。现在可以直接从 JS/TS 调用 Kotlin 的异步函数,无需额外代码。

要启用该特性,请在 build.gradle.kts 文件中添加以下编译器选项:

1
2
3
4
5
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xenable-suspend-function-exporting")
    }
}

启用后,带 @JsExport 注解的类和函数可以包含挂起函数,而无需额外的包装器。

它们可以像普通的 JavaScript 异步函数那样被使用,也可以被重写为异步函数:

1
2
3
4
@JsExport
open class Foo {
    suspend fun foo() = "Foo"
}
1
2
3
4
5
class Bar extends Foo {
    override async foo(): Promise<string> {
        return "Bar"
    }
}

该特性是实验性的。我们欢迎你在我们的问题跟踪器 YouTrack 中提供反馈。

使用 BigInt64Array 类型表示 Kotlin 的 LongArray 类型

实验性

以前,Kotlin/JS 把它的 LongArray 表示为 JavaScript 的 Array<bigint>。这种做法可行,但对期望类型化数组的 JavaScript API 来说并非理想的互操作方式。

从此版本开始,Kotlin/JS 在编译到 JavaScript 时改用了 JavaScript 内置的 BigInt64Array 类型来表示 Kotlin 的 LongArray 值。

使用 BigInt64Array 简化了与使用类型化数组的 JavaScript API 的互操作。它还让接受或返回 LongArray 的 API 能更自然地从 Kotlin 导出到 JavaScript。

要启用该特性,请在 build.gradle.kts 文件中添加以下编译器选项:

1
2
3
4
5
6
7
8
kotlin {
    js {
        // ...
        compilerOptions {
            freeCompilerArgs.add("-Xes-long-as-bigint")
        }
    }
}

该特性是实验性的。我们欢迎你在我们的问题跟踪器 YouTrack 中提供反馈。

跨 JS 模块系统统一伴生对象访问

以前,当你使用 @JsExport 注解把带伴生对象的 Kotlin 接口导出到 JavaScript/TypeScript 时,在 TypeScript 中消费该接口的方式在 ES 模块与其他模块系统之间有所不同。

结果,你不得不根据模块系统调整在 TypeScript 一侧消费输出的方式。

考虑这段 Kotlin 代码:

1
2
3
4
5
6
@JsExport
interface Foo {
    companion object {
        fun bar() = "OK"
    }
}

你必须根据模块系统以不同的方式调用它:

1
2
3
4
5
// 适用于 CommonJS、AMD、UMD 和无模块
Foo.bar()

// 适用于 ES 模块
Foo.getInstance().bar()

在此版本中,Kotlin 统一了所有 JavaScript 模块系统中的伴生对象导出。

现在对于每个模块系统(ES 模块、CommonJS、AMD、UMD、无模块),接口内的伴生对象总是以相同方式访问(就像类中的伴生对象一样):

1
2
// 适用于所有模块系统
Foo.Companion.bar()

这一改进还修复了集合互操作。以前,集合工厂函数必须根据模块系统以不同方式访问:

1
2
3
4
5
// 适用于 CommonJS、AMD、UMD 和无模块
KtList.fromJsArray([1, 2, 3])

// 适用于 ES 模块
KtList.getInstance().fromJsArray([1, 2, 3])

现在,访问集合工厂函数在所有模块系统中都类似:

1
2
// 适用于所有模块系统
KtList.fromJsArray([1, 2, 3])

这一变更减少了模块系统之间的不一致行为,避免了缺陷和互操作问题。

该特性默认启用。

支持在带伴生对象的接口中使用 JsStatic 注解

以前,不允许在导出的带伴生对象的接口内使用 @JsStatic 注解。

例如,以下代码会报错,因为只有类伴生对象的成员才能用 @JsStatic 注解:

1
2
3
4
5
6
7
@JsExport
interface Foo {
    companion object {
        @JsStatic // @JsStatic // 错误
        fun bar() = "OK"
    }
}

在这种情况下,你不得不去掉 @JsStatic 注解,并以如下方式从 JavaScript(JS)访问伴生对象:

1
2
// 适用于所有模块系统
Foo.Companion.bar()

现在,带伴生对象的接口中支持 @JsStatic 注解。你可以在这类伴生对象上使用该注解,并直接从 JS 调用该函数,就像对类所做的那样:

1
2
// 适用于所有模块系统
Foo.bar()

这一变更简化了在 JS 中消费 API,允许在接口上使用静态工厂方法,并消除了类与接口之间的不一致。

该特性默认启用。

允许在单个函数和类中使用 JsQualifier 注解

以前,你只能在文件级别应用 @JsQualifier 注解,这要求把所有外部 JavaScript(JS)声明放在单独的文件中。

从 Kotlin 2.3.0 开始,你可以像 @JsModule 和 @JsNonModule 注解那样,直接把 @JsQualifier 注解应用于单个函数和类。

例如,你现在可以在同一文件中把以下外部函数代码写在常规 Kotlin 声明旁边:

1
2
@JsQualifier("jsPackage")
private external fun jsFun()

这一变更简化了 Kotlin/JS 互操作,让你的项目结构更整洁,并使 Kotlin/JS 与其他平台处理外部声明的方式保持一致。

该特性默认启用。

支持 JavaScript 默认导出

以前,Kotlin/JS 无法从 Kotlin 代码生成 JavaScript 的默认导出。相反,Kotlin/JS 只生成命名导出,例如:

1
export { SomeDeclaration };

如果你需要默认导出,就必须在编译器中使用变通方案,例如放置带 default 和空格作为参数的 @JsName 注解:

1
2
3
@JsExport
@JsName("default ")
class SomeDeclaration

Kotlin/JS 现在通过一个新注解直接支持默认导出:

1
@JsExport.Default

当你把这个注解应用于 Kotlin 声明(类、对象、函数或属性)时,生成的 JavaScript 会自动为 ES 模块包含一条 export default 语句:

1
export default HelloWorker;

注意: 对于不同于 ES 模块的模块系统,新的 @JsExport.Default 注解的工作方式与常规的 @JsExport 注解类似。

这一变更使 Kotlin 代码能够符合 JavaScript 约定,对 Cloudflare Workers 之类的平台或 React.lazy 之类的框架尤为重要。

该特性默认启用。你只需要使用 @JsExport.Default 注解。

Gradle

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

此外,最低支持的 Android Gradle 插件版本现在是 8.2.2,最高支持版本是 8.13.0。

Kotlin 2.3.0 还为在 Gradle 项目中注册生成源引入了新 API。

在 Gradle 项目中注册生成源的新 API

实验性 - 通用

Kotlin 2.3.0 在 KotlinSourceSet 接口中引入了新的实验性 API,你可以用它来在 Gradle 项目中注册生成源。

这个新 API 是一项易用性改进,帮助 IDE 区分生成代码和常规源文件。该 API 允许 IDE 在 UI 中以不同方式高亮生成代码,并在项目导入时触发生成任务。我们目前正在 IntelliJ IDEA 中添加这一支持。该 API 对生成代码的第三方插件或工具(例如 KSP(Kotlin 符号处理))也特别有用。

更多信息请参见注册生成源。

标准库

Kotlin 2.3.0 稳定了新的时间跟踪功能 kotlin.time.Clock 和 kotlin.time.Instant,并为实验性 UUID API 添加了若干改进。

改进的 UUID 生成与解析

实验性

Kotlin 2.3.0 为 UUID API 引入了若干改进,包括:

标准库中的 UUID 支持是实验性的,但计划将来稳定。要选择启用,请使用 @OptIn(ExperimentalUuidApi::class) 注解,或在构建文件中添加以下编译器选项:

Gradle

1
2
3
4
5
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-opt-in=kotlin.uuid.ExperimentalUuidApi")
    }
}

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>-opt-in=kotlin.uuid.ExperimentalUuidApi</arg>
                </args>
            </configuration>
        </plugin>
    </plugins>
</build>

我们欢迎你在 YouTrack 或相关 Slack 频道中提供反馈。

解析无效 UUID 时返回 null 的支持

Kotlin 2.3.0 引入了从字符串创建 Uuid 实例的新函数,如果字符串不是有效的 UUID,它们会返回 null 而不是抛出异常。

这些函数包括:

  • Uuid.parseOrNull()——解析十六进制加短横线格式或纯十六进制格式的 UUID。
  • Uuid.parseHexDashOrNull()——只解析十六进制加短横线格式的 UUID,否则返回 null。
  • Uuid.parseHexOrNull()——只解析纯十六进制格式的 UUID,否则返回 null。

以下是一个示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid

@OptIn(ExperimentalUuidApi::class)
fun main() {
    val valid = Uuid.parseOrNull("550e8400-e29b-41d4-a716-446655440000")
    println(valid)
    // 550e8400-e29b-41d4-a716-446655440000

    val invalid = Uuid.parseOrNull("not-a-uuid")
    println(invalid)
    // null

    val hexDashValid = Uuid.parseHexDashOrNull("550e8400-e29b-41d4-a716-446655440000")
    println(hexDashValid)
    // 550e8400-e29b-41d4-a716-446655440000

    val hexDashInvalid = Uuid.parseHexDashOrNull("550e8400e29b41d4a716446655440000")
    println(hexDashInvalid)
    // null
}

生成 v4 和 v7 UUID 的新函数

Kotlin 2.3.0 引入了两个生成 UUID 的新函数:Uuid.generateV4() 和 Uuid.generateV7()。

使用 Uuid.generateV4() 函数生成版本 4 UUID,或使用 Uuid.generateV7() 函数生成版本 7 UUID。

注意: Uuid.random() 函数保持不变,仍然生成版本 4 UUID,与 Uuid.generateV4() 一样。

以下是一个示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid

@OptIn(ExperimentalUuidApi::class)
fun main() {
    // 生成一个 v4 UUID
    val v4 = Uuid.generateV4()
    println(v4)

    // 生成一个 v7 UUID
    val v7 = Uuid.generateV7()
    println(v7)

    // 生成一个 v4 UUID
    val random = Uuid.random()
    println(random)
}

为特定时间戳生成 v7 UUID 的支持

Kotlin 2.3.0 引入了新的 Uuid.generateV7NonMonotonicAt() 函数,你可以用它为特定的时间点生成版本 7 UUID。

注意: 与 Uuid.generateV7() 不同,Uuid.generateV7NonMonotonicAt() 不保证单调顺序,因此为同一时间戳创建的多个 UUID 可能不是顺序递增的。

当你需要与已知时间戳绑定的标识符时,可以使用该函数,例如重新创建事件 ID 或生成反映某件事最初发生时间的数据库条目。

例如,要为某个特定时刻创建版本 7 UUID,请使用以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
import kotlin.time.ExperimentalTime
import kotlin.time.Instant

@OptIn(ExperimentalUuidApi::class, ExperimentalTime::class)
fun main() {
    val timestamp = Instant.fromEpochMilliseconds(1577836800000) // 2020-01-01T00:00:00Z

    // 为指定时间戳生成一个 v7 UUID(不保证单调性)
    val v7AtTimestamp = Uuid.generateV7NonMonotonicAt(timestamp)
    println(v7AtTimestamp)
}

Compose 编译器:压缩后的 Android 应用的堆栈跟踪

从 Kotlin 2.3.0 开始,当应用被 R8 压缩时,编译器会为 Compose 堆栈跟踪输出 ProGuard 映射。这扩展了此前仅在可调试变体中可用的实验性堆栈跟踪特性。

堆栈跟踪的 release 变体包含组键,可在压缩后的应用中用来识别 composable 函数,而无需在运行时记录源信息的开销。组键堆栈跟踪要求你的应用使用 Compose 运行时 1.10 或更高版本构建。

要启用组键堆栈跟踪,请在初始化任何 @Composable 内容之前添加以下代码行:

1
Composer.setDiagnosticStackTraceMode(ComposeStackTraceMode.GroupKeys)

启用这些堆栈跟踪后,在组合、测量或绘制阶段捕获到崩溃时,Compose 运行时会追加自己的堆栈跟踪,即使应用已被压缩:

1
2
3
4
5
6
java.lang.IllegalStateException: <message>
        at <original trace>
    Suppressed: androidx.compose.runtime.DiagnosticComposeException: Composition stack when thrown:
        at $$compose.m$123(SourceFile:1)
        at $$compose.m$234(SourceFile:1)
        ...

在此模式下,Jetpack Compose 1.10 产生的堆栈跟踪只包含仍需反混淆的组键。Kotlin 2.3.0 版本通过 Compose Compiler Gradle 插件解决了这一问题,它现在会把组键条目追加到 R8 生成的 ProGuard 映射文件中。如果你在编译器无法为某些函数创建映射时看到新的警告,请把它们报告到 Google IssueTracker。

注意: 由于依赖于 R8 映射文件,Compose Compiler Gradle 插件只在构建启用了 R8 时才为组键堆栈跟踪创建反混淆映射。

默认情况下,无论你是否启用这些跟踪,映射文件 Gradle 任务都会运行。如果它们在你的构建中引发问题,你可以完全禁用该特性。请在 Gradle 配置的 composeCompiler {} 块中添加以下属性:

1
2
3
composeCompiler {
    includeComposeMappingFile.set(false)
}

警告: 存在一个已知问题:Android Gradle 插件提供的项目文件中有些代码不会出现在堆栈跟踪中:KT-83099。

请把遇到的任何问题报告到 Google IssueTracker。

破坏性变更与弃用

本节重点介绍重要的破坏性变更和弃用。完整概述请参见我们的兼容性指南。

  • 从 Kotlin 2.3.0 开始,编译器不再支持 -language-version=1.8。在非 JVM 平台上也不支持 -language-version=1.9。
  • 早于 2.0 的语言特性集不受支持(JVM 平台的 1.9 除外),但语言本身仍与 Kotlin 1.0 完全向后兼容。

如果你的 Gradle 项目同时使用 kotlin-dsl 和 kotlin("jvm") 插件,你可能会看到关于不受支持的 Kotlin 插件版本的 Gradle 警告。有关迁移步骤的指导,请参见我们的兼容性指南。

  • 在 Kotlin Multiplatform 中,对 Android 目标的支持现在通过 Google 的 com.android.kotlin.multiplatform.library 插件提供。请把带 Android 目标的项目迁移到新插件,并把 androidTarget 块重命名为 android。

  • 如果你继续在带 Android Gradle 插件(AGP)9.0.0 或更高版本的 Android 目标中使用 Kotlin Multiplatform Gradle 插件,在使用 androidTarget 块时你会看到配置错误,以及提供迁移指导的诊断消息。你可以通过使用 AGP 8.x 并更新到 Kotlin 2.3.10,或迁移到 Google 的 Android 目标插件来避免该错误。

  • AGP 9.0.0 包含对 Kotlin 的内置支持。从 Kotlin 2.3.0 开始,如果你把这个版本的 AGP 与 kotlin-android 插件一起使用,会看到配置错误,因为该插件已不再需要。我们提供了新的诊断消息帮助你迁移。如果你使用较旧的 AGP 版本,会看到弃用警告。

  • 不再提供对 Ant 构建系统的支持。

文档更新

Kotlin Multiplatform 文档已迁移到 kotlinlang.org。现在你可以在同一处切换 Kotlin 和 KMP 文档。我们还更新了语言指南的目录,并引入了新的导航。

自上一个 Kotlin 版本以来的其他值得注意的变更:

如何更新到 Kotlin 2.3.0

Kotlin 插件在 IntelliJ IDEA 和 Android Studio 中作为捆绑插件分发。

要更新到新的 Kotlin 版本,请在构建脚本中更改 Kotlin 版本为 2.3.0。