Skip to content

Kotlin 1.5.30 有哪些新功能

发布时间:2021 年 8 月 24 日

Kotlin 1.5.30 提供了语言更新,包括未来更改的预览、平台支持和工具的各种改进,以及新的标准库函数。

以下是一些主要改进:

  • 语言特性,包括实验性密封 when 语句、使用选择加入要求的更改等
  • 对 Apple 芯片的原生支持
  • Kotlin/JS IR 后端达到 Beta 阶段
  • 改进的 Gradle 插件体验

你还可以在发布博客文章和此视频中找到这些更改的简要概述:

语言特性

Kotlin 1.5.30 提供了未来语言更改的预览,并改进了选择加入要求机制和类型推断:

密封和布尔主题的详尽 when 语句

DANGER

密封 (详尽) when 语句的支持是实验性的。它可能随时被删除或更改。

需要选择加入(详见下文),并且你应仅将其用于评估目的。我们感谢你在 YouTrack 上提供反馈。

一个 详尽的 when 语句包含其主题所有可能类型或值的分支,或者包含特定类型的分支并包含一个 else 分支来覆盖任何剩余情况。

我们计划很快禁止非详尽的 when 语句,以使行为与 when 表达式保持一致。为确保平滑迁移,你可以配置编译器,使其报告关于密封类或布尔类型的非详尽 when 语句的警告。这些警告将默认出现在 Kotlin 1.6 中,并将在以后变为错误。

NOTE

枚举已经会收到警告。

kotlin
sealed class Mode {
    object ON : Mode()
    object OFF : Mode()
}

fun main() {
    val x: Mode = Mode.ON
    when (x) { 
        Mode.ON -> println("ON")
    }
// WARNING: Non exhaustive 'when' statements on sealed classes/interfaces 
// will be prohibited in 1.7, add an 'OFF' or 'else' branch instead

    val y: Boolean = true
    when (y) {  
        true -> println("true")
    }
// WARNING: Non exhaustive 'when' statements on Booleans will be prohibited 
// in 1.7, add a 'false' or 'else' branch instead
}

要在 Kotlin 1.5.30 中启用此功能,请使用语言版本 1.6。你还可以通过启用渐进模式将警告更改为错误。

kotlin
kotlin {
    sourceSets.all {
        languageSettings.apply {
            languageVersion = "1.6"
            //progressiveMode = true // false by default
        }
    }
}
groovy
kotlin {
    sourceSets.all {
        languageSettings {
            languageVersion = '1.6'
            //progressiveMode = true // false by default
        }
    }
}

挂起函数作为超类型

DANGER

挂起函数作为超类型的支持是实验性的。它可能随时被删除或更改。

需要选择加入(详见下文),并且你应仅将其用于评估目的。我们感谢你在 YouTrack 上提供反馈。

Kotlin 1.5.30 提供了在某些限制下使用 suspend 函数类型作为超类型的能力预览。

kotlin
class MyClass: suspend () -> Unit {
    override suspend fun invoke() { TODO() }
}

使用 -language-version 1.6 编译器选项来启用此功能:

kotlin
kotlin {
    sourceSets.all {
        languageSettings.apply {
            languageVersion = "1.6"
        }
    }
}
groovy
kotlin {
    sourceSets.all {
        languageSettings {
            languageVersion = '1.6'
        }
    }
}

该功能有以下限制:

  • 你不能将普通函数类型和 suspend 函数类型混合作为超类型。这是因为 suspend 函数类型在 JVM 后端中的实现细节。它们在其中表示为带有标记接口的普通函数类型。由于标记接口的存在,无法判断哪些超接口是挂起的,哪些是普通的。
  • 你不能使用多个 suspend 函数超类型。如果存在类型检查,你也不能使用多个普通函数超类型。

实验性 API 隐式使用需要选择加入

DANGER

选择加入要求机制是实验性的。

它可能随时更改。参阅如何选择加入

仅将其用于评估目的。我们感谢你在 YouTrack 上提供反馈。

库的作者可以将实验性 API 标记为需要选择加入,以告知用户其实验状态。当使用该 API 时,编译器会发出警告或错误,并要求明确同意以抑制警告或错误。

在 Kotlin 1.5.30 中,编译器将签名中具有实验性类型的任何声明视为实验性。也就是说,即使声明没有被明确标记为需要选择加入,它也要求对实验性 API 的隐式使用进行选择加入。例如,如果函数的返回类型被标记为实验性 API 元素,则即使该声明没有明确标记为需要选择加入,使用该函数也需要你选择加入。

kotlin
// Library code

@RequiresOptIn(message = "This API is experimental.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
annotation class MyDateTime // Opt-in requirement annotation

@MyDateTime
class DateProvider // A class requiring opt-in

// Client code

// Warning: experimental API usage
fun createDateSource(): DateProvider { /* ... */ }

fun getDate(): Date {
    val dateSource = createDateSource() // Also warning: experimental API usage
    // ... 
}

了解更多关于选择加入要求的信息。

使用带不同目标的选择加入要求注解的更改

DANGER

选择加入要求机制是实验性的。

它可能随时更改。参阅如何选择加入

仅将其用于评估目的。我们感谢你在 YouTrack 上提供反馈。

Kotlin 1.5.30 提出了关于在不同目标上使用和声明选择加入要求注解的新规则。编译器现在会报告在编译时难以处理的使用情况的错误。在 Kotlin 1.5.30 中:

  • 在使用处禁止使用选择加入要求注解标记局部变量和值参数。
  • 只有当其基本声明也被标记时才允许标记覆盖。
  • 禁止标记支持字段和获取器。你可以改为标记基本属性。
  • 在选择加入要求注解声明处禁止设置 TYPETYPE_PARAMETER 注解目标。

了解更多关于选择加入要求的信息。

递归泛型类型类型推断的改进

在 Kotlin 和 Java 中,你可以定义一个递归泛型类型,它在其类型参数中引用自身。在 Kotlin 1.5.30 中,Kotlin 编译器可以仅根据相应类型参数的上限来推断类型参数,如果它是一个递归泛型。这使得使用递归泛型类型创建各种模式成为可能,这些模式在 Java 中经常用于构建器 API。

kotlin
// Kotlin 1.5.20
val containerA = PostgreSQLContainer<Nothing>(DockerImageName.parse("postgres:13-alpine")).apply {
    withDatabaseName("db")
    withUsername("user")
    withPassword("password")
    withInitScript("sql/schema.sql")
}

// Kotlin 1.5.30
val containerB = PostgreSQLContainer(DockerImageName.parse("postgres:13-alpine"))
    .withDatabaseName("db")
    .withUsername("user")
    .withPassword("password")
    .withInitScript("sql/schema.sql")

你可以通过传递 -Xself-upper-bound-inference-language-version 1.6 编译器选项来启用这些改进。在此 YouTrack 议题中查看新支持使用情况的其他示例。

消除构建器推断限制

构建器推断是一种特殊的类型推断,它允许你根据其 Lambda 参数中其他调用的类型信息来推断调用的类型参数。这在调用泛型构建器函数时非常有用,例如 buildList()sequence()buildList { add("string") }

在此类 Lambda 参数内部,以前对使用构建器推断尝试推断的类型信息存在限制。这意味着你只能指定它而不能获取它。例如,你不能在 buildList() 的 Lambda 参数内部调用 get() 而不显式指定类型参数。

Kotlin 1.5.30 通过 -Xunrestricted-builder-inference 编译器选项消除了这些限制。添加此选项以启用以前在泛型构建器函数的 Lambda 参数内部被禁止的调用:

kotlin
@kotlin.ExperimentalStdlibApi
val list = buildList {
    add("a")
    add("b")
    set(1, null)
    val x = get(1)
    if (x != null) {
        removeAt(1)
    }
}

@kotlin.ExperimentalStdlibApi
val map = buildMap {
    put("a", 1)
    put("b", 1.1)
    put("c", 2f)
}

此外,你还可以使用 -language-version 1.6 编译器选项启用此功能。

Kotlin/JVM

在 Kotlin 1.5.30 中,Kotlin/JVM 获得了以下功能:

有关 JVM 平台上 Kotlin Gradle 插件更新的信息,请参阅 Gradle 部分。

注解类的实例化

DANGER

注解类的实例化是实验性的。它可能随时被删除或更改。

需要选择加入(详见下文),并且你应仅将其用于评估目的。我们感谢你在 YouTrack 上提供反馈。

使用 Kotlin 1.5.30,你现在可以在任意代码中调用注解类的构造函数以获取结果实例。此功能涵盖了与 Java 约定相同的用例,Java 约定允许实现注解接口。

kotlin
annotation class InfoMarker(val info: String)

fun processInfo(marker: InfoMarker) = ...

fun main(args: Array<String>) {
    if (args.size != 0)
        processInfo(getAnnotationReflective(args))
    else
        processInfo(InfoMarker("default"))
}

使用 -language-version 1.6 编译器选项来启用此功能。请注意,所有当前的注解类限制,例如禁止定义非 val 参数或与次构造函数不同的成员,都保持不变。

this KEEP 中了解更多关于注解类实例化的信息。

改进的可空性注解支持配置

Kotlin 编译器可以读取各种类型的可空性注解以从 Java 获取可空性信息。这些信息允许它在调用 Java 代码时报告 Kotlin 中的可空性不匹配。

在 Kotlin 1.5.30 中,你可以根据来自特定类型的可空性注解的信息,指定编译器是否报告可空性不匹配。只需使用编译器选项 -Xnullability-annotations=@<package-name>:<report-level>。在参数中,指定完全限定的可空性注解包和以下报告级别之一:

  • ignore 忽略可空性不匹配
  • warn 报告警告
  • strict 报告错误。

请参阅支持的可空性注解的完整列表及其完全限定的包名。

以下是一个示例,展示如何为新支持的 RxJava 3 可空性注解启用错误报告:[email protected]:strict。请注意,所有此类可空性不匹配默认为警告。

Kotlin/Native

Kotlin/Native 收到了各种更改和改进:

Apple 芯片支持

Kotlin 1.5.30 引入了对 Apple 芯片的原生支持。

以前,Kotlin/Native 编译器和工具需要在 Apple 芯片主机上运行 Rosetta 转换环境。在 Kotlin 1.5.30 中,不再需要转换环境——编译器和工具可以在 Apple 芯片硬件上运行而无需任何额外操作。

我们还引入了新的目标,使 Kotlin 代码在 Apple 芯片上原生运行:

  • macosArm64
  • iosSimulatorArm64
  • watchosSimulatorArm64
  • tvosSimulatorArm64

它们在基于 Intel 和 Apple 芯片主机上都可用。所有现有目标在 Apple 芯片主机上也可用。

请注意,在 1.5.30 中,我们仅在 kotlin-multiplatform Gradle 插件中提供对 Apple 芯片目标的基本支持。特别是,新的模拟器目标不包含在 iostvoswatchos 目标快捷方式中。 我们将继续努力改进新目标的用户体验。

改进的 CocoaPods Gradle 插件的 Kotlin DSL

Kotlin/Native 框架的新参数

Kotlin 1.5.30 引入了针对 Kotlin/Native 框架改进的 CocoaPods Gradle 插件 DSL。除了框架名称之外,你还可以在 Pod 配置中指定其他参数:

  • 指定框架的动态或静态版本
  • 显式启用依赖项导出
  • 启用 Bitcode 嵌入

要使用新的 DSL,请将你的项目更新到 Kotlin 1.5.30,并在 build.gradle(.kts) 文件的 cocoapods 部分中指定参数:

kotlin
cocoapods {
    frameworkName = "MyFramework" // This property is deprecated 
    // and will be removed in future versions
    // New DSL for framework configuration:
    framework {
        // All Framework properties are supported
        // Framework name configuration. Use this property instead of 
        // deprecated 'frameworkName'
        baseName = "MyFramework"
        // Dynamic framework support
        isStatic = false
        // Dependency export
        export(project(":anotherKMMModule"))
        transitiveExport = false // This is default.
        // Bitcode embedding
        embedBitcode(BITCODE)
    }
}

支持 Xcode 配置的自定义名称

Kotlin CocoaPods Gradle 插件支持 Xcode 构建配置中的自定义名称。如果你在 Xcode 中使用特殊名称进行构建配置,例如 Staging,它也将有所帮助。

要指定自定义名称,请在 build.gradle(.kts) 文件的 cocoapods 部分中使用 xcodeConfigurationToNativeBuildType 参数:

kotlin
cocoapods {
    // Maps custom Xcode configuration to NativeBuildType
    xcodeConfigurationToNativeBuildType["CUSTOM_DEBUG"] = NativeBuildType.DEBUG
    xcodeConfigurationToNativeBuildType["CUSTOM_RELEASE"] = NativeBuildType.RELEASE
}

此参数不会出现在 Podspec 文件中。当 Xcode 运行 Gradle 构建过程时,Kotlin CocoaPods Gradle 插件将选择所需的原生构建类型。

NOTE

无需声明 DebugRelease 配置,因为它们默认受支持。

与 Swift 5.5 async/await 的实验性互操作性

DANGER

与 Swift async/await 的并发互操作性是实验性的。它可能随时被删除或更改。

你应仅将其用于评估目的。我们感谢你在 YouTrack 上提供反馈。

我们在 1.4.0 中添加了从 Objective-C 和 Swift 调用 Kotlin 挂起函数的支持,现在我们正在改进它,以跟上 Swift 5.5 的新功能——使用 asyncawait 修饰符的并发

Kotlin/Native 编译器现在会为具有可空返回类型的挂起函数在生成的 Objective-C 头文件中发出 _Nullable_result 属性。这使得它们可以从 Swift 作为具有正确可空性的 async 函数进行调用。

请注意,此功能是实验性的,未来可能会受到 Kotlin 和 Swift 更改的影响。目前,我们提供了此功能的预览版,它具有一定的限制,我们非常渴望听取你的意见。在此 YouTrack 议题中了解其当前状态并留下你的反馈。

改进的对象和伴生对象的 Swift/Objective-C 映射

现在可以使用对原生 iOS 开发者来说更直观的方式获取对象和伴生对象。例如,如果你在 Kotlin 中有以下对象:

kotlin
object MyObject {
    val x = "Some value"
}

class MyClass {
    companion object {
        val x = "Some value"
    }
}

要在 Swift 中访问它们,你可以使用 sharedcompanion 属性:

swift
MyObject.shared
MyObject.shared.x
MyClass.companion
MyClass.Companion.shared

了解更多关于 Swift/Objective-C 互操作性的信息。

MinGW 目标对不带导入库的 DLL 链接的弃用

LLD 是 LLVM 项目中的链接器,我们计划开始在 Kotlin/Native 中将其用于 MinGW 目标,因为它相对于默认的 ld.bfd 有更好的性能——主要是其更好的性能。

然而,LLD 的最新稳定版本不支持 MinGW (Windows) 目标直接链接到 DLL。这种链接需要使用导入库。尽管 Kotlin/Native 1.5.30 不需要它们,但我们添加了一个警告,告知你这种用法与未来将成为 MinGW 默认链接器的 LLD 不兼容。

请在此 YouTrack 议题中分享你对转换为 LLD 链接器的想法和疑虑。

Kotlin Multiplatform

1.5.30 为 Kotlin Multiplatform 带来了以下显著更新:

在共享原生代码中使用自定义 cinterop 库的能力

Kotlin Multiplatform 提供了一个选项,可以在共享源集中使用平台相关的互操作库。在 1.5.30 之前,这只适用于 Kotlin/Native 分发版随附的平台库。从 1.5.30 开始,你可以将其与你的自定义 cinterop 库一起使用。要启用此功能,请在 gradle.properties 中添加 kotlin.mpp.enableCInteropCommonization=true 属性:

none
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.enableDependencyPropagation=false
kotlin.mpp.enableCInteropCommonization=true

支持 XCFrameworks

所有 Kotlin Multiplatform 项目现在都可以将 XCFrameworks 作为输出格式。Apple 引入 XCFrameworks 作为通用 (fat) 框架的替代品。借助 XCFrameworks,你:

  • 可以将所有目标平台和架构的逻辑收集到一个单一捆绑包中。
  • 无需在将应用程序发布到 App Store 之前删除所有不必要的架构。

如果你想在 Apple M1 设备和模拟器上使用 Kotlin 框架,XCFrameworks 会很有用。

要使用 XCFrameworks,请更新你的 build.gradle(.kts) 脚本:

kotlin
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework

plugins {
    kotlin("multiplatform")
}

kotlin {
    val xcf = XCFramework()
  
    ios {
        binaries.framework {
            baseName = "shared"
            xcf.add(this)
        }
    }
    watchos {
        binaries.framework {
            baseName = "shared"
            xcf.add(this)
        }
    }
    tvos {
        binaries.framework {
            baseName = "shared"
            xcf.add(this)
        }
    }
}
groovy
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFrameworkConfig

plugins {
    id 'org.jetbrains.kotlin.multiplatform'
}

kotlin {
    def xcf = new XCFrameworkConfig(project)

    ios {
        binaries.framework {
            baseName = "shared"
            xcf.add(it)
        }
    }
    watchos {
        binaries.framework {
            baseName = "shared"
            xcf.add(it)
        }
    }
    tvos {
        binaries.framework {
            baseName = "shared"
            xcf.add(it)
        }
    }
}

当你声明 XCFrameworks 时,将注册以下新的 Gradle 任务:

  • assembleXCFramework
  • assembleDebugXCFramework (额外包含 dSYMs 的 debug artifact)
  • assembleReleaseXCFramework

this WWDC video 中了解更多关于 XCFrameworks 的信息。

Android artifacts 的新默认发布设置

使用 maven-publish Gradle 插件,你可以通过在构建脚本中指定 Android 变体名称来为 Android 目标发布多平台库。Kotlin Gradle 插件将自动生成发布。

在 1.5.30 之前,生成的发布元数据包含每个已发布 Android 变体的构建类型属性,使其仅与库使用者使用的相同构建类型兼容。Kotlin 1.5.30 引入了一个新的默认发布设置:

  • 如果项目发布的所有 Android 变体都具有相同的构建类型属性,则已发布的变体将不具有构建类型属性,并将与任何构建类型兼容。
  • 如果已发布的变体具有不同的构建类型属性,则只有具有 release 值的变体将在没有构建类型属性的情况下发布。这使得发布变体与使用者侧的任何构建类型兼容,而非发布变体将仅与匹配的使用者构建类型兼容。

要选择退出并为所有变体保留构建类型属性,你可以设置此 Gradle 属性:kotlin.android.buildTypeAttribute.keep=true

Kotlin/JS

Kotlin 1.5.30 为 Kotlin/JS 带来了两项主要改进:

JS IR 编译器后端达到 Beta 阶段

Kotlin/JS 的 基于 IR 的编译器后端,在 1.4.0 中以 Alpha 阶段引入,现已达到 Beta 阶段。

此前,我们发布了 JS IR 后端迁移指南,以帮助你将项目迁移到新的后端。现在我们想介绍 Kotlin/JS Inspection Pack IDE 插件,它直接在 IntelliJ IDEA 中显示所需的更改。

使用 Kotlin/JS IR 后端改进应用程序的调试体验

Kotlin 1.5.30 为 Kotlin/JS IR 后端带来了 JavaScript 源映射生成。当启用 IR 后端时,这将改进 Kotlin/JS 调试体验,提供完整的调试支持,包括断点、单步调试和具有正确源引用的可读堆栈跟踪。

了解如何在浏览器或 IntelliJ IDEA Ultimate 中调试 Kotlin/JS

Gradle

作为我们改进 Kotlin Gradle 插件用户体验任务的一部分,我们实现了以下功能:

支持 Java 工具链

Gradle 6.7 引入了“Java 工具链支持”功能。 使用此功能,你可以:

  • 使用与 Gradle 不同的 JDK 和 JRE 运行编译、测试和可执行文件。
  • 使用未发布的语言版本编译和测试代码。

通过工具链支持,Gradle 可以自动检测本地 JDK 并安装构建所需的缺失 JDK。现在 Gradle 本身可以在任何 JDK 上运行,并且仍然可以重用构建缓存功能

Kotlin Gradle 插件支持 Kotlin/JVM 编译任务的 Java 工具链。 Java 工具链:

使用以下代码设置工具链。将占位符 <MAJOR_JDK_VERSION> 替换为你想要使用的 JDK 版本:

kotlin
kotlin {
    jvmToolchain {
        (this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // "8"
    }
}
groovy
kotlin {
    jvmToolchain {
        languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // "8"
    }
}

请注意,通过 kotlin 扩展设置工具链也会更新 Java 编译任务的工具链。

你可以通过 java 扩展设置工具链,Kotlin 编译任务将使用它:

kotlin
java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // "8"
    }
}

有关为 KotlinCompile 任务设置任何 JDK 版本的信息,请查阅使用 Task DSL 设置 JDK 版本的文档。

对于 Gradle 6.1 到 6.6 版本,使用 UsesKotlinJavaToolchain 接口设置 JDK 主目录

使用 UsesKotlinJavaToolchain 接口指定 JDK 主目录的能力

所有支持通过 kotlinOptions 设置 JDK 的 Kotlin 任务现在都实现了 UsesKotlinJavaToolchain 接口。要设置 JDK 主目录,请提供你的 JDK 路径并替换 <JDK_VERSION> 占位符:

kotlin
project.tasks
    .withType<UsesKotlinJavaToolchain>()
    .configureEach {
        it.kotlinJavaToolchain.jdk.use(
            "/path/to/local/jdk",
            JavaVersion.<LOCAL_JDK_VERSION>
        )
    }
groovy
project.tasks
    .withType(UsesKotlinJavaToolchain.class)
    .configureEach {
        it.kotlinJavaToolchain.jdk.use(
            '/path/to/local/jdk',
            JavaVersion.<LOCAL_JDK_VERSION>
        )
    }

对于 Gradle 6.1 到 6.6 版本,请使用 UsesKotlinJavaToolchain 接口。从 Gradle 6.7 开始,请使用 Java 工具链代替。

使用此功能时,请注意 kapt 任务工作进程将只使用进程隔离模式,并且 kapt.workers.isolation 属性将被忽略。

更简单地显式指定 Kotlin Daemon JVM 参数的方法

在 Kotlin 1.5.30 中,Kotlin Daemon 的 JVM 参数有了新的逻辑。以下列表中的每个选项都会覆盖其之前的选项:

  • 如果未指定任何内容,Kotlin Daemon 将继承 Gradle Daemon 的参数(与以前相同)。例如,在 gradle.properties 文件中:

    none
    org.gradle.jvmargs=-Xmx1500m -Xms=500m
  • 如果 Gradle Daemon 的 JVM 参数包含 kotlin.daemon.jvm.options 系统属性,则照常使用它:

    none
    org.gradle.jvmargs=-Dkotlin.daemon.jvm.options=-Xmx1500m -Xms=500m
  • 你可以在 gradle.properties 文件中添加 kotlin.daemon.jvmargs 属性:

    none
    kotlin.daemon.jvmargs=-Xmx1500m -Xms=500m
  • 你可以在 kotlin 扩展中指定参数:

kotlin
    kotlin {
        kotlinDaemonJvmArgs = listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC")
    }
    ```
```groovy [Groovy]
    kotlin {
        kotlinDaemonJvmArgs = ["-Xmx486m", "-Xms256m", "-XX:+UseParallelGC"]
    }
    ```
:::

* 你可以为特定任务指定参数:

    ::: code-group
```kotlin [Kotlin]
    tasks
        .matching { it.name == "compileKotlin" && it is CompileUsingKotlinDaemon }
        .configureEach {
            (this as CompileUsingKotlinDaemon).kotlinDaemonJvmArguments.set(listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC"))
        }
    ```
```groovy [Groovy]
    tasks
        .matching {
            it.name == "compileKotlin" && it instanceof CompileUsingKotlinDaemon
        }
        .configureEach {
            kotlinDaemonJvmArguments.set(["-Xmx1g", "-Xms512m"])
        }
    ```
:::

    ::: note

    在这种情况下,新的 Kotlin Daemon 实例可以在任务执行时启动。了解更多关于[Kotlin Daemon 与 JVM 参数的交互](gradle-compilation-and-caches.md#setting-kotlin-daemon-s-jvm-arguments)的信息。

    :::
有关 Kotlin Daemon 的更多信息,请参阅[Kotlin Daemon 及其与 Gradle 的使用](gradle-compilation-and-caches.md#the-kotlin-daemon-and-how-to-use-it-with-gradle)。

## 标准库

Kotlin 1.5.30 正在为标准库的 `Duration` 和 `Regex` API 带来改进:
* [`Duration.toString()` 输出的更改](#changing-duration-tostring-output)
* [从字符串解析 Duration](#parsing-duration-from-string)
* [在特定位置使用 Regex 匹配](#matching-with-regex-at-a-particular-position)
* [将 Regex 拆分为序列](#splitting-regex-to-a-sequence)

### `Duration.toString()` 输出的更改

::: warning

Duration API 是[实验性](components-stability.md)的。它可能随时被删除或更改。

仅将其用于评估目的。我们感谢你在 [YouTrack](https://youtrack.jetbrains.com/issues/KT) 上提供反馈。

:::
在 Kotlin 1.5.30 之前,[`Duration.toString()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/to-string.html) 函数会返回其参数的字符串表示,该字符串表示以产生最紧凑和可读数字值的单位表示。
从现在起,它将返回一个以数字组件组合表示的字符串值,每个组件都有自己的单位。
每个组件都是一个数字,后跟单位的缩写名称:`d`、`h`、`m`、`s`。例如:

|**函数调用示例**|**先前输出**|**当前输出**|
| --- | --- | --- |
Duration.days(45).toString()|`45.0d`|`45d`|
Duration.days(1.5).toString()|`36.0h`|`1d 12h`|
Duration.minutes(1230).toString()|`20.5h`|`20h 30m`|
Duration.minutes(2415).toString()|`40.3h`|`1d 16h 15m`|
Duration.minutes(920).toString()|`920m`|`15h 20m`|
Duration.seconds(1.546).toString()|`1.55s`|`1.546s`|
Duration.milliseconds(25.12).toString()|`25.1ms`|`25.12ms`|

负持续时间的表示方式也已更改。负持续时间以负号 (`-`) 为前缀,如果它由多个组件组成,则用括号括起来:`-12m` 和 `-(1h 30m)`。

请注意,小于一秒的短持续时间表示为单个数字,带有亚秒单位之一。例如,`ms`(毫秒)、`us`(微秒)或 `ns`(纳秒):`140.884ms`、`500us`、`24ns`。不再使用科学计数法表示它们。

如果你想以单个单位表示持续时间,请使用重载的 `Duration.toString(unit, decimals)` 函数。

::: note

在某些情况下,包括序列化和交换,我们建议使用 [`Duration.toIsoString()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/to-iso-string.html)。`Duration.toIsoString()` 使用更严格的 [ISO-8601](https://www.iso.org/iso-8601-date-and-time-format.html) 格式,而不是 `Duration.toString()`。

:::
### 从字符串解析 Duration

::: warning

Duration API 是[实验性](components-stability.md)的。它可能随时被删除或更改。

仅将其用于评估目的。我们感谢你在[此议题](https://github.com/Kotlin/KEEP/issues/190)中提供反馈。

:::
在 Kotlin 1.5.30 中,Duration API 中有新函数:
* [`parse()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/parse.html),支持解析以下输出:
    * [`toString()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/to-string.html)。
    * [`toString(unit, decimals)`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/to-string.html)。
    * [`toIsoString()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/to-iso-string.html)。
* [`parseIsoString()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/parse-iso-string.html),它只从 `toIsoString()` 生成的格式进行解析。
* [`parseOrNull()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/parse-or-null.html) 和 [`parseIsoStringOrNull()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.time/-duration/parse-iso-string-or-null.html),它们的行为与上述函数类似,但对于无效的 Duration 格式返回 `null` 而不是抛出 `IllegalArgumentException`。

以下是一些 `parse()` 和 `parseOrNull()` 用法的示例:

```kotlin
import kotlin.time.Duration
import kotlin.time.ExperimentalTime

@ExperimentalTime
fun main() {
    val isoFormatString = "PT1H30M"
    val defaultFormatString = "1h 30m"
    val singleUnitFormatString = "1.5h"
    val invalidFormatString = "1 hour 30 minutes"
    println(Duration.parse(isoFormatString)) // "1h 30m"
    println(Duration.parse(defaultFormatString)) // "1h 30m"
    println(Duration.parse(singleUnitFormatString)) // "1h 30m"
    //println(Duration.parse(invalidFormatString)) // throws exception
    println(Duration.parseOrNull(invalidFormatString)) // "null"
}

以下是一些 parseIsoString()parseIsoStringOrNull() 用法的示例:

kotlin
import kotlin.time.Duration
import kotlin.time.ExperimentalTime

@ExperimentalTime
fun main() {
    val isoFormatString = "PT1H30M"
    val defaultFormatString = "1h 30m"
    println(Duration.parseIsoString(isoFormatString)) // "1h 30m"
    //println(Duration.parseIsoString(defaultFormatString)) // throws exception
    println(Duration.parseIsoStringOrNull(defaultFormatString)) // "null"
}

在特定位置使用 Regex 匹配

DANGER

Regex.matchAt()Regex.matchesAt() 函数是实验性的。它们可能随时被删除或更改。

仅将其用于评估目的。我们感谢你在 YouTrack 上提供反馈。

新的 Regex.matchAt()Regex.matchesAt() 函数提供了一种方法,用于检查正则表达式是否在 StringCharSequence 中的特定位置具有精确匹配。

matchesAt() 返回一个布尔结果:

kotlin
fun main(){
    val releaseText = "Kotlin 1.5.30 is released!"
    // regular expression: one digit, dot, one digit, dot, one or more digits
    val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()
    println(versionRegex.matchesAt(releaseText, 0)) // "false"
    println(versionRegex.matchesAt(releaseText, 7)) // "true"
}

matchAt() 如果找到匹配则返回匹配,否则返回 null

kotlin
fun main(){
    val releaseText = "Kotlin 1.5.30 is released!"
    val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()
    println(versionRegex.matchAt(releaseText, 0)) // "null"
    println(versionRegex.matchAt(releaseText, 7)?.value) // "1.5.30"
}

将 Regex 拆分为序列

DANGER

Regex.splitToSequence()CharSequence.splitToSequence(Regex) 函数是实验性的。它们可能随时被删除或更改。

仅将其用于评估目的。我们感谢你在 YouTrack 上提供反馈。

新的 Regex.splitToSequence() 函数是 split() 的惰性对应项。它根据给定正则表达式的匹配项拆分字符串,但它将结果作为 Sequence 返回,以便对该结果的所有操作都惰性执行。

kotlin
fun main(){
    val colorsText = "green, red , brown&blue, orange, pink&green"
    val regex = "[,\\s]+".toRegex()
    val mixedColor = regex.splitToSequence(colorsText)
        .onEach { println(it) }
        .firstOrNull { it.contains('&') }
    println(mixedColor) // "brown&blue"
}

CharSequence 也添加了一个类似函数:

kotlin
    val mixedColor = colorsText.splitToSequence(regex)

Serialization 1.3.0-RC

kotlinx.serialization 1.3.0-RC 带来了新的 JSON 序列化功能:

  • Java IO 流序列化
  • 属性级别控制默认值
  • 排除空值序列化的选项
  • 多态序列化中的自定义类鉴别器

变更日志中了解更多信息。