Kotlin 1.5.30 版中的新功能
最终更新: 2024/03/21
Kotlin 1.5.30 带来语言更新, 包括功能变更的预览, 平台支持与工具方面的大量改进, 以及新的标准库函数.
下面是主要的功能改进:
- 语言功能, 包括封闭式(sealed)
when
语句(实验性功能), 明确要求使用者同意的功能(Opt-in Requirement)的使用方法变更, 以及其他更新 - 支持 Apple Silicon 平台的原生(Native)开发
- Kotlin/JS IR 后端升级为 Beta 版
- Gradle plugin 使用体验改进
关于功能变更的简短介绍, 也可以参见 发布公告, 以及下面的视频:
语言功能
Kotlin 1.5.30 提供了未来的语言功能变更的预览, 并带来了要求使用者同意的功能(Opt-in Requirement)和类型推断的改进:
- 针对封闭类或布尔值的穷尽式(exhaustive) when 语句
- 挂起函数用作超类型
- 隐含使用实验性 API 时要求使用者同意
- 要求使用者同意注解对不同目标的使用方式的变更
- 对递归泛型类型的类型推断的改进
- 去掉了构建器推断的限制
针对封闭类或布尔值的穷尽式(exhaustive) when 语句
封闭 (穷尽式) when 语句是 实验性功能. 它随时有可能变更或被删除. 需要使用者同意(Opt-in) (详情见下文), 请注意, 只为评估和试验目的来使用这个功能. 希望你能通过我们的 问题追踪系统 提供你的反馈意见.
一个 穷尽式 when
语句包含对应于所有可能的类型或值的分支, 对于某些类型, 再加上 else
分支.
也就是说, 它覆盖私有可能的情况.
我们计划很快禁用非穷尽的 when
语句, 使得 when
语句的动作与 when
表达式一致.
为了保证平滑移植, 你可以配置编译器, 对封闭类或布尔值的非穷尽 when
语句报告警告.
在 Kotlin 1.6 中默认会出现这些警告, 将来会变为错误.
枚举类型已经有了这些警告.
sealed class Mode {
object ON : Mode()
object OFF : Mode()
}
fun main() {
val x: Mode = Mode.ON
when (x) {
Mode.ON -> println("ON")
}
// 编译器警告: 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")
}
// 编译器警告: 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 {
sourceSets.all {
languageSettings.apply {
languageVersion = "1.6"
//progressiveMode = true // 默认为 false
}
}
}
kotlin {
sourceSets.all {
languageSettings {
languageVersion = '1.6'
//progressiveMode = true // 默认为 false
}
}
}
挂起函数用作超类型
挂起函数用作超类型是 实验性功能. 它随时有可能变更或被删除. 需要使用者同意(Opt-in) (详情见下文). 请注意, 只为评估和试验目的来使用这个功能. 希望你能通过我们的 问题追踪系统 提供你的反馈意见.
Kotlin 1.5.30 提供了一个功能预览, 可以将一个 suspend
函数类型用作一个超类型, 但存在一些限制.
class MyClass: suspend () -> Unit {
override suspend fun invoke() { TODO() }
}
使用 -language-version 1.6
编译器选项来启用这个功能:
kotlin {
sourceSets.all {
languageSettings.apply {
languageVersion = "1.6"
}
}
}
kotlin {
sourceSets.all {
languageSettings {
languageVersion = '1.6'
}
}
}
这个功能存在以下限制:
- 在超类型中, 你不能混合使用一个通常的函数类型与一个的
suspend
函数类型. 这是由于suspend
函数类型在 JVM 后端中的实现细节造成的. 它表达为一个通常的函数类型加上一个标记接口. 由于这个标记接口, 无法区分哪个父接口是挂起函数, 哪个是通常函数. - 你不能使用多个
suspend
函数作为超类型. 如果存在类型检查, 你也不能使用多个通常的函数作为超类型.
隐含使用实验性 API 时要求使用者同意
要求使用者同意机制是 实验性功能. 它随时有可能变更或被删除. 参见 如何明确要求使用者同意. 请注意, 只为评估和试验目的来使用这个功能. 希望你能通过我们的 问题追踪系统 提供你的反馈意见.
库的作者可以将一个试验性 API 标记为 要求使用者同意, 用来提醒使用者这个功能处于试验性状态. 当使用这个 API 时, 编译器会报告一个警告或错误, 并要求 明确同意使用 来消除这些警告或错误.
在 Kotlin 1.5.30 中, 对于签名中存在试验性类型的任何声明, 编译器都认为它们是试验性的. 也就是说, 即使对一个试验性 API 的隐含使用, 它也要求使用者同意. 比如, 如果函数的返回类型标记为试验性 API 元素, 那么使用这个函数会也要求你明确同意, 即使函数声明本身明确没有标记为需要使用者同意.
// 库代码
@RequiresOptIn(message = "This API is experimental.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
annotation class MyDateTime // 要求使用者同意的注解
@MyDateTime
class DateProvider // 一个要求使用者同意的类
// 客户端代码
// 编译器警告: experimental API usage
fun createDateSource(): DateProvider { /* ... */ }
fun getDate(): Date {
val dateSource = createDateSource() // 这里也会出现编译器警告: experimental API usage
// ...
}
详情请参见 明确要求使用者同意.
要求使用者同意注解对不同目标的使用方式的变更
要求使用者同意机制是 实验性功能. 它随时有可能变更或被删除. 参见 如何明确要求使用者同意. 请注意, 只为评估和试验目的来使用这个功能. 希望你能通过我们的 问题追踪系统 提供你的反馈意见.
在不同的 注解目标 上的要求使用者同意的注解的使用和声明, Kotlin 1.5.30 现在使用新的规则. 对于编译期无法处理的使用场景, 编译器现在会报告错误. 在 Kotlin 1.5.30 中:
- 在使用端, 禁止对局部变量和值参数标注要求使用者同意的注解.
- 对 override, 只有当它的原始声明也进行了标注, 才允许进行标注.
- 禁止对后端域和 get 方法标注. 你可以改为标注属性.
- 在要求使用者同意的注解的声明端, 禁止将注解目标设置为
TYPE
和TYPE_PARAMETER
.
详情请参见 明确要求使用者同意的功能.
对递归泛型类型的类型推断的改进
在 Kotlin 和 Java 中, 你可以定义一个递归泛型类型, 在它的类型参数中引用它自身. 在 Kotlin 1.5.30 中, 如果一个类型参数是递归泛型, 那么 Kotlin 编译器可以只根据对应的类型参数的上界(Upper Bound)推断出这个类型参数. 因此, 可以使用递归泛型类型, 创建出 Java 中经常用来创建构建器 API 的很多模式.
// 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 ticket.
去掉了构建器推断的限制
构建器推断一种特殊的类型推断, 可以根据一个调用的 Lambda 参数之内的其他调用的类型信息, 来推断这个调用的类型参数.
当调用泛型构建器函数时, 这个功能可以很有用,
比如 buildList()
或 sequence()
:
buildList { add("string") }
.
在这样一个 Lambda 参数内部, 以前曾经存在一个限制, 不能使用构建器推断功能尝试推断的类型信息.
因此你只能指定这个类型信息, 而不能通过推断得到它. 比如, 在 buildList()
的 Lambda 参数之内,
除非明确指定类型参数, 否则你不能调用 get()
.
Kotlin 1.5.30 使用 -Xunrestricted-builder-inference
编译器选项去掉了这个限制.
添加这个选项, 可以在泛型构建器函数的 Lambda 参数之内, 启用以前被禁止的调用:
@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 新增了以下功能:
See the Gradle section for Kotlin Gradle plugin updates on the JVM platform.
创建注解类的实例
创建注解类的实例是 实验性功能. 它随时有可能变更或被删除. 需要使用者同意(Opt-in) (详情见下文). 请注意, 只为评估和试验目的来使用这个功能. 希望你能通过我们的 问题追踪系统 提供你的反馈意见.
在 Kotlin 1.5.30 中, 现在你可以在任何代码中调用 注解类 的构造器, 来获得一个实例. 这个功能能够用于 Java 中相同的使用场景, 可以实现一个注解接口.
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
参数, 或与次级构造器(secondary constructor)不同的成员.
关于创建注解类的实例, 更多详情请参见 这个 KEEP.
可否为 null(Nullability) 注解的支持配置的改进
Kotlin 编译器可以读取多种类型的 可否为 null(Nullability) 注解, 来从 Java 代码得到可否为 null 信息. 这个信息使得它能够报告 Kotlin 中调用 Java 代码时的可否为 null 不匹配的错误.
在 Kotlin 1.5.30 中, 你可以指定编译器是否根据指定的可否为 null 注解类型的信息来报告可否为 null 不匹配的错误.
只需要使用编译器选项 -Xnullability-annotations=@<package-name>:<report-level>
.
在参数中, 指定可否为 null 注解的全限定包名称, 以及以下报告级别中的一个:
ignore
: 忽略可否为 null 不匹配warn
: 报告为警告strict
: 报告为错误.
详情请参见 支持的可否为 null 注解列表 以及它们的全限定包名称.
下面是一个示例, 演示如何对新支持的 RxJava 3 可否为 null 注解启用错误报告:
-Xnullability-annotations=@io.reactivex.rxjava3.annotations:strict
.
注意, 所有这些可否为 null 不匹配, 默认设置为警告.
Kotlin/Native
Kotlin/Native 包含以下变更和改进:
- 支持 Apple Silicon
- CocoaPods Gradle plugin 的 Kotlin DSL 的改进
- 与 Swift 5.5 async/await 的交互(实验性功能)
- 对象和伴随对象到 Swift/Objective-C 的映射的改进
- 对 MinGW 编译目标废弃无导入库的 DLL 链接
支持 Apple Silicon
Kotlin 1.5.30 引入了对 Apple Silicon 的原生支持.
在以前的版本中, Kotlin/Native 编译器和工具需要 Rosetta 翻译环境 才能在 Apple Silicon 主机上工作. 在 Kotlin 1.5.30 中, 不再需要翻译环境 – 编译器和工具可以在 Apple Silicon 硬件上运行, 不需要任何额外的操作.
我们还引入了新的编译目标, 可以使 Kotlin 代码在 Apple Silicon 上直接运行:
macosArm64
iosSimulatorArm64
watchosSimulatorArm64
tvosSimulatorArm64
这些编译目标可以用于 Intel 和 Apple Silicon 的主机. 所有既有的编译目标也可以在 Apple Silicon 主机上使用.
注意, 在 1.5.30 中, 我们只在 kotlin-multiplatform
Gradle plugin 中提供对 Apple Silicon 编译目标的基本的支持.
具体来说, 在 ios
, tvos
, 和 watchos
编译目标简写(target shortcut) 中没有包含新的模拟器编译目标.
我们会继续改进这些新的编译目标的使用体验.
CocoaPods Gradle plugin 的 Kotlin DSL 的改进
Kotlin/Native Framework 的新参数
Kotlin 1.5.30 带来了 CocoaPods Gradle plugin DSL 关于 Kotlin/Native Framework 的改进. 除了 Framework 名称之外, 你还可以在 Pod 配置中指定其他参数:
- 指定 Framework 的动态或静态版本
- 明确启用导出依赖项
- 启用 Bitcode 内嵌
要使用新的 DSL, 请将你的项目更新到 Kotlin 1.5.30, 并在你的 build.gradle(.kts)
文件的 cocoapods
节中指定参数:
cocoapods {
frameworkName = "MyFramework" // 这个属性已废弃, 并会在将来的版本中删除
// Framework 配置的新的 DSL 如下:
framework {
// 支持所有的 Framework 属性
// Framework 名称配置. 使用这个属性代替已废弃的 'frameworkName'
baseName = "MyFramework"
// 支持动态 Framework
isStatic = false
// 依赖项导出
export(project(":anotherKMMModule"))
transitiveExport = false // 这是默认设置.
// Bitcode 内嵌
embedBitcode(BITCODE)
}
}
对 Xcode 配置支持自定义名称
Kotlin CocoaPods Gradle plugin 在 Xcode 构建配置中支持自定义名称.
如果你在 Xcode 中为构建配置使用了特殊的名称, 比如 Staging
, 这个功能也可以帮助你.
要指定一个自定义名称, 请在你的 build.gradle(.kts)
文件的 cocoapods
节中使用 xcodeConfigurationToNativeBuildType
参数:
cocoapods {
// 将自定义的 Xcode 配置映射到 NativeBuildType
xcodeConfigurationToNativeBuildType["CUSTOM_DEBUG"] = NativeBuildType.DEBUG
xcodeConfigurationToNativeBuildType["CUSTOM_RELEASE"] = NativeBuildType.RELEASE
}
这个参数不会出现在 Podspec 文件中. 当 Xcode 运行 Gradle 构建过程时, Kotlin CocoaPods Gradle plugin 会选择必要的原生构建类型.
不需要声明
Debug
和Release
配置, 因为默认支持它们.
与 Swift 5.5 async/await 的交互(实验性功能)
与 Swift async/await 的并发交互是 实验性功能. 它随时有可能变更或被删除. 请注意, 只为评估和试验目的来使用这个功能. 希望你能通过我们的 问题追踪系统 提供你的反馈意见.
过去我们曾经 在 1.4.0 中添加了从 Objective-C 和 Swift 中调用 Kotlin 关起函数的功能,
现在我们删除这个功能, 改为使用新的 Swift 5.5 功能 – 使用 async
和 await
修饰符的并发功能.
对于返回类型可为 null 的挂起函数, Kotlin/Native 编译器现在会在生成的 Objective-C 头文件中, 输出 _Nullable_result
属性.
因此在 Swift 中可以将这些函数作为 async
函数来调用, 并且得到正确的可否为 null 结果.
注意, 这个功能是实验性功能, 未来可能由于 Kotlin 和 Swift 的变化而受到影响. 目前来说, 我们提供这个功能的一个预览版, 带有一些限制, 我们期待得到你的意见反馈. 请在 这个 YouTrack issue 中查看这个功能目前的状态, 并留下你的反馈意见.
对象和伴随对象到 Swift/Objective-C 的映射的改进
对于原生 iOS 开发者来说, 现在可以通过更加符合直觉的方式得到对象和伴随对象. 比如, 如果在 Kotlin 中你有以下对象:
object MyObject {
val x = "Some value"
}
class MyClass {
companion object {
val x = "Some value"
}
}
要在 Swift 中访问它们, 你可以使用 shared
和 companion
属性:
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 来说不需要它们, 但我们添加了一个警告, 告知你这样的使用不兼容于 LLD, 将来 LLD 会成为 MinGW 编译目标的默认链接器.
关于转换到 LLD 浏览器, 请在 这个 YouTrack issue 中提供你的反馈意见.
Kotlin Multiplatform
1.5.30 对于 Kotlin Multiplatform 带来了以下重要更新:
在共用的原生代码中可以使用自定义 cinterop
库
Kotlin Multiplatform 提供了一个 选项,
可以在共用的源代码集中使用平台相关的 interop 库.
在 1.5.30 之前, 这个功能只能用于随 Kotlin/Native 一同发布的 平台库.
从 1.5.30 开始, 你可以使用你自定义的 cinterop
库.
要启用这个功能, 请在你的 gradle.properties
中添加 kotlin.mpp.enableCInteropCommonization=true
属性:
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.enableDependencyPropagation=false
kotlin.mpp.enableCInteropCommonization=true
支持 XCFramework
所有的 Kotlin Multiplatform 项目现在可以使用 XCFramework 作为输出格式. Apple 引入了 XCFramework 来替代通用(Universal) (fat) Framework. 通过使用 XCFramework, 你:
- 可以将所有的编译目标平台和处理器架构的逻辑集中在一个单独的 bundle 中.
- 在将应用程序发布到 App Store 之前, 不必删除所有不需要的处理器架构.
如果你想要对 Apple M1 上的设备和模拟器使用你的 Kotlin Framework, XCFramework 会很有用.
要使用 XCFramework, 请更新你的 build.gradle(.kts)
脚本:
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)
}
}
}
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)
}
}
}
当你声明 XCFramework 时, 会注册这些新的 Gradle task:
assembleXCFramework
assembleDebugXCFramework
(debug 用 artifact, 包含 dSYMs)assembleReleaseXCFramework
关于 XCFramework, 详情请参见 这个 WWDC 视频.
对 Android artifact 的新的默认发布设置
使用 maven-publish
Gradle plugin, 你可以在构建脚本中指定 Android 变体 名称,
对 Android 编译目标发布你的跨平台库.
Kotlin Gradle plugin 会自动生成发布.
在 1.5.30 之前, 生成的发布 metadata 包含每一个发布的 Android 变体的构建类型属性, 因此只能兼容于库使用者所使用的相同的构建类型. Kotlin 1.5.30 引入了一个新的默认发布设置:
- 如果项目发布的所有 Android 变体拥有相同的构建类型属性, 那么发布的变体不会拥有构建类型属性, 而且能够兼容于任何构建类型.
- 如果发布的变体拥有不同的构建类型属性, 那么只有带有
release
值的, 发布时会不带有构建类型属性. 因此 release 变体兼容于使者端的任何构建类型, 而 release 之外的其他变体只兼容于匹配的使者端构建类型.
如果要关闭这个功能, 并对所有变体保持构建类型属性, 你可以设置这个 Gradle 属性:
kotlin.android.buildTypeAttribute.keep=true
.
Kotlin/JS
在 1.5.30 中, Kotlin/JS 有 2 个主要改进:
JS IR 编译器后端升级为 Beta 版
1.4.0 版引入了 Kotlin/JS 的 基于 IR 的编译器后端, 当时是 Alpha 版, 现在升级为 Beta 版.
以前, 我们发布了 移植到 JS IR 后端的向导, 来帮助你将你的项目移植到新的后端. 现在我们提供 Kotlin/JS Inspection Pack IDE plugin, 它可以直接在 IntelliJ IDEA 中显示需要哪些修改.
使用 Kotlin/JS IR 后端为应用程序带来更好的调试体验
Kotlin 1.5.30 带来了对 Kotlin/JS IR 后端的 JavaScript 源代码映射生成功能. 这个功能可以改善启用 IR 后端时的 Kotlin/JS 调试体验, 支持所有的调试功能, 包括断点, 单步执行, 以及易读的调用栈信息, 带有正确的源代码引用.
详情请参见 如何在浏览器中或在 IntelliJ IDEA Ultimate 中调试 Kotlin/JS.
Gradle
为了 改进 Kotlin Gradle plugin 使用者体验, 我们实现了以下功能:
- 支持 Java 工具链, 包括 可以使用
UsesKotlinJavaToolchain
接口对 Gradle 旧版本指定 JDK Home - 用更简单的方式明确指定 Kotlin Daemon 的 JVM 参数
支持 Java 工具链
Gradle 6.7 引入了 支持 Java 工具链 功能. 使用这个功能, 你可以:
- 使用与 Gradle 不同的 JDK 和 JRE 运行编译, 测试, 和可执行文件.
- 使用未发布的语言版本编译和测试代码.
通过工具链支持, Gradle 可以自动检测本地的 JDK, 并安装 Gradle 构建所需要但缺失的 JDK. 现在 Gradle 自身可以在任何 JDK 上运行, 而且还能够重用 构建缓存功能.
Kotlin Gradle plugin 对 Kotlin/JVM 编译任务支持 Java 工具链. Java 工具链会:
- 为 JVM 编译目标设置可用的
jdkHome
选项. -
如果使用者没有明确设置
jvmTarget
选项, 会将kotlinOptions.jvmTarget
设置为工具链的 JDK 版本. 如果工具链没有配置,jvmTarget
域会使用默认值. 详情请参见 JVM 编译目标兼容性. - 影响
kapt
worker 运行在哪个 JDK 上.
可以使用以下代码来设置一个工具链. 请将 <MAJOR_JDK_VERSION>
替换为你想要使用的 JDK 版本:
kotlin {
jvmToolchain {
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // "8"
}
}
kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // "8"
}
}
注意, 通过 kotlin
扩展设置工具链, 也会对 Java 编译任务更新工具链.
你可以通过 java
扩展设置工具链, Kotlin 编译任务会使用它:
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // "8"
}
}
关于为 KotlinCompile
任务设置 JDK 版本, 请参见 使用 Task DSL 设置 JDK 版本.
对于 Gradle 版本 6.1 到 6.6, 请 使用 UsesKotlinJavaToolchain
接口来设置 JDK Home.
使用 UsesKotlinJavaToolchain 接口指定 JDK Home
所有支持通过 kotlinOptions
设置 JDK 的 Kotlin 任务,
现在都实现 UsesKotlinJavaToolchain
接口.
要设置 JDK Home, 请设置你的 JDK 路径, 并替换 <JDK_VERSION>
部分:
project.tasks
.withType<UsesKotlinJavaToolchain>()
.configureEach {
it.kotlinJavaToolchain.jdk.use(
"/path/to/local/jdk",
JavaVersion.<LOCAL_JDK_VERSION>
)
}
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 任务 worker 只使用 进程隔离模式, kapt.workers.isolation
属性将被忽略.
用更简单的方式明确指定 Kotlin Daemon 的 JVM 参数
在 Kotlin 1.5.30 中, 对于 Kotlin Daemon 的 JVM 参数有了新的逻辑. 以下列表中的每个选项都会覆盖它之前的选项:
-
如果没有指定任何参数, Kotlin Daemon 会从 Gradle Daemon 继承参数(和以前一样). 比如, 在
gradle.properties
文件中:org.gradle.jvmargs=-Xmx1500m -Xms=500m
-
如果 Gradle Daemon 的 JVM 参数包含
kotlin.daemon.jvm.options
系统属性, 和以前一样使用它:org.gradle.jvmargs=-Dkotlin.daemon.jvm.options=-Xmx1500m -Xms=500m
-
在
gradle.properties
文件中你可以添加kotlin.daemon.jvmargs
属性:kotlin.daemon.jvmargs=-Xmx1500m -Xms=500m
-
你可以在
kotlin
扩展中指定参数:kotlin { kotlinDaemonJvmArgs = listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC") }
kotlin { kotlinDaemonJvmArgs = ["-Xmx486m", "-Xms256m", "-XX:+UseParallelGC"] }
-
你可以对一个特定的任务指定参数 :
tasks .matching { it.name == "compileKotlin" && it is CompileUsingKotlinDaemon } .configureEach { (this as CompileUsingKotlinDaemon).kotlinDaemonJvmArguments.set(listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC")) }
tasks .matching { it.name == "compileKotlin" && it instanceof CompileUsingKotlinDaemon } .configureEach { kotlinDaemonJvmArguments.set(["-Xmx1g", "-Xms512m"]) }
在这种情况中, 在任务执行时可以启动一个新的 Kotlin Daemon 实例. 详情请参见 Kotlin Daemon 与 JVM 参数的交互.
关于 Kotlin Daemon, 详情请参见 Kotlin Daemon 以及它在 Gradle 中的使用.
标准库
Kotlin 1.5.30 包括对标准库的 Duration
和 Regex
API 的改进:
改变了 Duration.toString() 的输出
Duration API 是 实验性功能. 它随时有可能变更或被删除. 请注意, 只为评估和试验目的来使用这个功能. 希望你能通过我们的 问题追踪系统 提供你的反馈意见.
在 Kotlin 1.5.30 以前,
Duration.toString()
函数会返回它的参数的字符串表达, 使用最紧凑的并且可以阅读的数值单位.
从现在开始, 它会返回一个字符串值, 表达为多个数值部分的组合, 每个数值部分使用自己的单位.
每个部分是一个数值, 加上一个单位缩写名称: 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)
.
注意, 少于 1 秒的时间长度表达为单个数值, 使用秒以下单位的. 比如, ms
(毫秒), us
(微秒), 或 ns
(纳秒): 140.884ms
, 500us
, 24ns
.
不再使用科学记数法来表达.
如果你想要使用单个单位来表达时间长度, 请使用超载函数 Duration.toString(unit, decimals)
.
在某些情况下, 我们推荐使用
Duration.toIsoString()
, 包括序列化和数据交换.Duration.toIsoString()
使用更加严格的 ISO-8601 格式, 而不是Duration.toString()
.
从字符串解析 Duration
Duration API 是 实验性功能. 它随时有可能变更或被删除. 请注意, 只为评估和试验目的来使用这个功能. 希望你能通过我们的 这个 issue 提供你的反馈意见.
在 Kotlin 1.5.30 中, 有以下新的 Duration API 函数:
parse()
, 支持解析以下函数的输出:parseIsoString()
, 只解析toIsoString()
输出的格式.parseOrNull()
和parseIsoStringOrNull()
, 与上面的函数类似, 但对于无效的 Duration 格式, 会返回null
而不是抛出IllegalArgumentException
异常.
下面是 parse()
和 parseOrNull()
的一些使用示例:
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@ExperimentalTime
fun main() {
//sampleStart
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)) // 抛出异常
println(Duration.parseOrNull(invalidFormatString)) // 输出为 "null"
//sampleEnd
}
下面是 parseIsoString()
和 parseIsoStringOrNull()
的一些使用示例:
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@ExperimentalTime
fun main() {
//sampleStart
val isoFormatString = "PT1H30M"
val defaultFormatString = "1h 30m"
println(Duration.parseIsoString(isoFormatString)) // 输出为 "1h 30m"
//println(Duration.parseIsoString(defaultFormatString)) // 抛出异常
println(Duration.parseIsoStringOrNull(defaultFormatString)) // 输出为 "null"
//sampleEnd
}
在一个指定的位置匹配正规表达式
Regex.matchAt()
和Regex.matchesAt()
函数是 实验性功能. 它随时有可能变更或被删除. 请注意, 只为评估和试验目的来使用这个功能. 希望你能通过我们的 问题追踪系统 提供你的反馈意见.
新的 Regex.matchAt()
和 Regex.matchesAt()
函数提供一种方法,
可以检查一个正规表达式在一个 String
或 CharSequence
的指定位置是否存在完全匹配.
matchesAt()
返回一个 boolean 结果:
fun main(){
//sampleStart
val releaseText = "Kotlin 1.5.30 is released!"
// 正规表达式: 1个数字, 点, 1个数字, 点, 1个或多个数字
val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()
println(versionRegex.matchesAt(releaseText, 0)) // 输出为 "false"
println(versionRegex.matchesAt(releaseText, 7)) // 输出为 "true"
//sampleEnd
}
如果找到匹配, 则 matchAt()
返回匹配结果, 否则返回 null
:
fun main(){
//sampleStart
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"
//sampleEnd
}
使用正规表达式将字符串切分为一个序列
Regex.splitToSequence()
和CharSequence.splitToSequence(Regex)
函数是 实验性功能. 它随时有可能变更或被删除. 请注意, 只为评估和试验目的来使用这个功能. 希望你能通过我们的 问题追踪系统 提供你的反馈意见.
新的 Regex.splitToSequence()
函数是
split()
函数的 lazy 版本.
它根据正规表达式的匹配结果切分字符串, 但结果返回为一个 序列(Sequence),
因此对这个结果的所有操作都会以 lazy 模式执行.
fun main(){
//sampleStart
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"
//sampleEnd
}
对 CharSequence
也添加了一个类似的函数 :
val mixedColor = colorsText.splitToSequence(regex)
Serialization 的 1.3.0-RC 版
发布了 kotlinx.serialization
1.3.0-RC 版,
包括新的 JSON 序列化功能:
- Java IO Stream 序列化
- 对默认值的属性级控制
- 一个选项, 可以在序列化中排除 null 值
- 在多态序列化中使用自定义类区分
详情请参见 changelog.