Kotlin 1.4.30 版中的新功能
Kotlin 1.4.30 提供了新的语言功能的预览版, 将 Kotlin/JVM 编译器的新的 IR 后端升级到 Beta, 并带来了很多性能和功能的改进.
关于这个版本的变更概要, 可以查看 这篇 blog.
语言功能
Kotlin 1.5.0 将会发布一些新的语言功能 – 支持 JVM 记录类(Record), 封闭接口(Sealed Interface), 以及内联类(Inline Class)的稳定版. 在 Kotlin 1.4.30 中, 你可以通过预览模式试用这些新功能和新改进. 如果你能够在相应的 YouTrack ticket 中提供你的反馈意见, 我们将会非常感谢, 你的反馈能够帮助我们在 1.5.0 正式发布之前解决这些问题.
要通过预览模式启用这些新功能和新改进, 你需要添加特定的编译器选项, 来表示你明确同意使用. 详情请阅读下面的章节.
关于新功能预览, 详情请参见 这篇 blog.
支持 JVM 记录类(Record)
JDK 16 版 中计划稳定新类型的 Java 类, 名为 Record. 为了充分利用 Kotlin 的功能, 并保证与 Java 的交互能力, Kotlin 会增加对记录类的支持(实验性功能).
你可以在 Kotlin 中使用 Java 中声明的记录类, 就和其他有属性的类一样. 不需要其他任何步骤.
从 1.4.30 开始, 你可以在 Kotlin 中对一个 数据类 使用 @JvmRecord
注解, 来声明记录类:
要试用 JVM 记录类功能的预览版, 请添加编译器选项 -Xjvm-enable-preview
和 -language-version 1.5
.
我们还在继续完善这个功能, 如果你能通过这个 YouTrack ticket 提供你的反馈意见, 我们会非常感谢.
关于这个功能的具体实现, 限制, 以及语法, 请参见 KEEP.
封闭接口(Sealed Interface)
在 Kotlin 1.4.30 中, 我们发布了 封闭接口(Sealed Interface) 的原型. 这个功能是对封闭类的补充, 可以用来构建更加灵活的类层级关系约束.
封闭接口可以用作 "internal" 接口, 不能在同一个模块之外实现. 你可以利用这一点来实现很多功能, 比如, 编写穷尽式(exhaustive) when
表达式.
另一种使用场景是: 使用封闭接口, 你可以从两个或多个封闭的超类继承一个类.
要试用封闭接口的预览版, 请添加编译器选项 -language-version 1.5
. 切换到这个版本之后, 你就可以对接口使用 sealed
修饰符了. 如果你能通过这个 YouTrack ticket 提供你的反馈意见, 我们会非常感谢.
更多详情请参见 封闭接口(Sealed Interface).
包范围内的封闭类(Sealed Class)层级结构
封闭类(Sealed Class)现在可以组成更加灵活的层级结构. 子类可以存在于同一个编译单元同一个包的所有源代码文件中. 在以前的版本中, 所有子类必须存在于同一个源代码文件中.
直接子类可以是顶层类, 也可以内嵌在任意数量的其他命名类, 命名接口, 或命名对象之内. 封闭类的子类必须拥有适当的限定名称 – 不能是局部类, 也不能是匿名对象.
要试用包范围内的封闭类层级结构功能, 请添加编译器选项 -language-version 1.5
. 如果你能通过这个 YouTrack ticket 提供你的反馈意见, 我们会非常感谢.
更多详情请参见 包范围内的封闭类(Sealed Class)层级结构.
内联类(Inline Class)的改进
Kotlin 1.4.30 将 内联类(Inline Class) 升级到 Beta 版, 并带来以下功能和改进:
由于内联类是 基于值的, 因此你可以使用
value
修饰符来定义内联类.inline
和value
修饰符现在是相互等价的. 在未来的 Kotlin 版本中, 我们计划废弃inline
修饰符.从现在开始, 对于 JVM 后端, Kotlin 要求在类的声明之前添加
@JvmInline
注解:inline class Name(private val s: String) value class Name(private val s: String) // 对于 JVM 后端 @JvmInline value class Name(private val s: String)内联类可以拥有
init
代码段. 你可以添加需要在类实例创建之后立即执行的代码:@JvmInline value class Negative(val x: Int) { init { require(x < 0) { } } }在 Java 代码中使用内联类调用函数: 在 Kotlin 1.4.30 之前, 由于代码混淆(mangle), 你不能从 Java 代码中调用接受内联类参数的函数. 从现在开始, 你可以手动关闭代码混淆. 要从 Java 代码调用这样的函数, 你需要在函数声明前添加
@JvmName
注解:inline class UInt(val x: Int) fun compute(x: Int) { } @JvmName("computeUInt") fun compute(x: UInt) { }在这个发布版中, 我们修改了对函数的代码混淆机制, 以便修正一些不正确的行为. 这些变更会导致 ABI 变化.
从 1.4.30 开始, Kotlin 编译器默认使用新的代码混淆机制. 可以使用
-Xuse-14-inline-classes-mangling-scheme
编译器 flag 来强制编译器使用使用旧的 1.4.0 代码混淆机制, 以保证二进制兼容性.
Kotlin 1.4.30 将内联类升级为 Beta 版, 我们计划在未来的发布版中将它升级为稳定版. 如果你能通过这个 YouTrack ticket 提供你的反馈意见, 我们会非常感谢.
要试用内联类的预览版, 请添加编译器选项 -Xinline-classes
或 -language-version 1.5
.
关于代码混淆算法, 更多详情请参见 KEEP.
更多详情请参见 内联类.
Kotlin/JVM
JVM IR 编译器后端升级为 Beta 版
Kotlin/JVM 的 基于 IR 的编译器后端, 在 1.4.0 版引入时是 Alpha 版, 现在升级为 Beta 版. 这是稳定版之前的最后一个测试版, 稳定版发布后, Kotlin/JVM 编译器会默认使用 IR 后端.
我们现在会去掉使用 IR 编译器产生的二进制文件的限制. 以前的版本中, 你必须启用新的后端, 然后才能使用新的 JVM IR 后端编译的代码. 从 1.4.30 开始, 不再存在这样的限制, 因此你可以使用新的后端来构建供第三方使用的组件, 比如库. 请试用新后端的 Beta 版本, 并通过我们的 issue tracker 提供你的反馈意见.
要启用新的 JVM IR 后端, 请向项目的构建脚本添加以下设置:
Gradle:
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile::class) { kotlinOptions.useIR = true }tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile) { kotlinOptions.useIR = true }Maven:
<configuration> <args> <arg>-Xuse-ir</arg> </args> </configuration>
关于 JVM IR 后端的变更, 更多详情请参见 这篇 blog.
Kotlin/Native
性能改善
在 1.4.30 中, Kotlin/Native 有了很多性能改善, 使得编译速度更加提升. 比如, 在 使用 Kotlin Multiplatform Mobile 开发的网络和数据存储 示例项目中, 重新构建框架所需要的时间从 9.5 秒 (1.4.10 版) 减少到了 4.5 秒 (1.4.30 版).
Apple watchOS 64-bit 模拟器编译目标
从 watchOS 版本 7.0 开始, x86 模拟器编译目标已被废弃. 为与 watchOS 的最新版本保持一致, Kotlin/Native 增加了新的编译目标 watchosX64
, 用于在 64-bit 架构运行模拟器.
支持 Xcode 12.2 库
我们增加了对随 Xcode 12.2 发布的新的库的支持. 你现在可以在 Kotlin 代码中使用这些库了.
Kotlin/JS
顶级属性(top-level property)的延迟初始化(Lazy initialization)
Kotlin/JS 的 IR 后端 有了顶级属性(top-level property)的延迟初始化(Lazy initialization)功能的原型实现. 这个功能可以在应用程序启动时减少需要初始化的顶级属性, 可以显著改善应用程序的启动时间.
我们会继续改进延迟初始化功能, 我们希望你试用目前的原型实现, 并通过这个 YouTrack ticket 或官方 Kotlin Slack (请在 这里 得到邀请) 的 #javascript
频道, 反馈你的想法和试用结果.
要使用延迟初始化功能, 请在使用 JS IR 编译器编译代码时添加 -Xir-property-lazy-initialization
编译器选项.
Gradle 项目的改进
支持 Gradle 配置缓存
从 1.4.30 开始, Kotlin Gradle plugin 支持 配置缓存 功能. 这个功能会提高构建过程的速度: 一旦你执行命令, Gradle 会执行配置过程, 并计算任务图(task graph). Gradle 会缓存计算结果, 并在以后的构建中重用这些结果.
要启用这个功能, 你可以 使用 Gradle 命令 或 设置 IntelliJ based IDE.
标准库
针对大写/小写文字的 Locale 无关 API
本次发布版增加了改变字符串和字符大小写的 Locale 无关 API (实验性功能). 现在的 toLowerCase()
, toUpperCase()
, capitalize()
, decapitalize()
API 函数是与 Locale 相关的. 也就是说, 不同的平台 Locale 设置可能影响代码的行为. 比如, 在 Turkish locale 中, 使用 toUpperCase
来转换字符串 "kotlin", 结果会是 "KOTLİN", 而不是 "KOTLIN".
Kotlin 1.4.30 提供了以下替代函数:
对
String
函数:以前的版本
1.4.30 的替代函数
String.toUpperCase()
String.uppercase()
String.toLowerCase()
String.lowercase()
String.capitalize()
String.replaceFirstChar { it.uppercase() }
String.decapitalize()
String.replaceFirstChar { it.lowercase() }
Char
函数:以前的版本
1.4.30 的替代函数
Char.toUpperCase()
Char.uppercaseChar(): Char
Char.uppercase(): String
Char.toLowerCase()
Char.lowercaseChar(): Char
Char.lowercase(): String
Char.toTitleCase()
Char.titlecaseChar(): Char
Char.titlecase(): String
关于文字处理函数的所有变更, 请参见 KEEP.
明确的 "字符到代码" 和 "字符到数值" 转换
目前的 Char
到数值的转换函数, 返回不同数值类型表达的 UTF-16 代码, 经常会与类似的字符串到整数值的转换混淆, 后一种转换会返回字符串表示的数值:
为了避免这样的混淆, 我们决定将 Char
转换分离为以下两组名称更加清晰的函数:
第一组的函数, 用于得到
Char
的整数代码, 以及通过指定的代码构建Char
:fun Char(code: Int): Char fun Char(code: UShort): Char val Char.code: Int第二组的函数, 将
Char
转换为它所表达的数字的整数值:fun Char.digitToInt(radix: Int): Int fun Char.digitToIntOrNull(radix: Int): Int?Int
的一个扩展函数, 可以将整数表达的单个非负数字, 转换为对应的Char
表达:fun Int.digitToChar(radix: Int): Char
更多详情请参见 KEEP.
序列化库的更新
随 Kotlin 1.4.30 一起, 我们还发布了 kotlinx.serialization
1.1.0-RC, 其中包含一些新功能:
支持内联类的序列化
支持无符号基本类型(Unsigned Primitive Type)的序列化
支持内联类的序列化
从 Kotlin 1.4.30 开始, 你可以让内联类 可序列化:
当可序列化内联类被用在另一个可序列化类之内时, 序列化框架不会对可序列化内联类装箱.
更多详情请参见 kotlinx.serialization
的 文档.
支持无符号基本类型(Unsigned Primitive Type)的序列化
从 1.4.30 开始, 你可以对无符号基本类型(Unsigned Primitive Type): UInt
, ULong
, UByte
, 和 UShort
, 使用 kotlinx.serialization 的标准的 JSON 序列化器:
更多详情请参见 kotlinx.serialization
的 文档.