Kotlin 1.7.0 重大更新

17,218 阅读18分钟

前言

Kotlin 已经发布了1.7.0版本,该版本带来了新Kotlin/JVM K2编译器的Alpha版本,稳定了语言特性,并为JVM、JS 和 Native平台带来了性能改进。

主要更新列表

以下是此版本的主要更新列表:

详细介绍

Alpha 版中 用于 JVM 的 Kotlin K2 编译器

这个版本引入了新的Kotlin K2编译器的 Alpha 版本。新编译器旨在加快新语言功能的开发,统一Kotlin支持的所有平台,带来性能改进,并为编译器扩展提供API。

目前已经发布的一些有关新编译器和其优点的详细说明见下列内容:

目前,对于新的K2编译器的 Alpha 版本。主要关注与性能改进,并仅适用于JVM项目。目前不支持 Kotlin/JS、Kotlin/Native 或其他多平台项目,包括kapt在内的任何编译器插件都不能使用它。

在项目基准测试中,新编译器的出色表现:

ProjectCurrent Kotlin compiler performanceNew K2 Kotlin compiler performancePerformance boost
Kotlin2.2 KLOC/s4.8 KLOC/s~ x2.2
YouTrack1.8 KLOC/s4.2 KLOC/s~ x2.3
IntelliJ IDEA1.8 KLOC/s3.9 KLOC/s~ x2.2
Space1.2 KLOC/s2.8 KLOC/s~ x2.3

KLOC/s 性能数字代表编译器每秒处理的数千行代码的数量

你也可以检验在JVM项目中性能提升,并将其与旧编译器的结果相比较。

如果要启用Kotlin K2编译器,请使用以下编译器选项:

-Xuse-k2

此外,K2编译器还包括了许多错误修复。注意:即使在列表中状态为open的issue问题,实际也已经在K2编译器中修复。

下一个Kotlin版本将提高K2编译器的稳定性并提供更多功能。

语言方面

Kotlin 1.7.0 引入了对委托实现的支持以及用于类型参数的新下划线运算符。还稳定了以前版本中作为预览引入的几个语言功特性:

允许通过委托实现内联类的内联值

在1.7.0之前,如果要为值或类实例创建轻量级包装器,需要手动实现所有接口方法。委托实现解决了这个问题,此限制已被删除,现在你可以创建在大多数情况下不分配内存的轻量级包装器。

interface Bar {
    fun foo() = "foo"
}

@JvmInline
value class BarWrapper(val bar: Bar): Bar by bar

fun main() {
    val bw = BarWrapper(object: Bar {})
    println(bw.foo())
}

类型参数的下划线运算符

Kotlin 1.7.0为类型参数引入了下划线运算符_,当指定其他类型时,可以使用它来自动推断类型参数:

abstract class SomeClass<T> {
    abstract fun execute(): T
}

class SomeImplementation : SomeClass<String>() {
    override fun execute(): String = "Test"
}

class OtherImplementation : SomeClass<Int>() {
    override fun execute(): Int = 42
}

object Runner {
    inline fun <reified S: SomeClass<T>, T> run(): T {
        return S::class.java.getDeclaredConstructor().newInstance().execute()
    }
}

fun main() {
    // T is inferred as String because SomeImplementation derives from SomeClass<String>
    val s = Runner.run<SomeImplementation, _>()
    assert(s == "Test")

    // T is inferred as Int because OtherImplementation derives from SomeClass<Int>
    val n = Runner.run<OtherImplementation, _>()
    assert(n == 42)
}

现在可以在变量列表中的任何位置使用下划线运算符来推断类型参数。

构造器推断

构建器推断是一种特殊的类型推断,在调用通用构建器函数时很有用。在其lambda参数中使用有关其他调用的类型信息时,它帮助编译器推断调用的类型参数。

从1.7.0开始,如果没有指定-Xenable-builder-inference编译器选项(在1.6.0中引入),常规类型推断无法获得有关类型的足够信息,则会自动激活构建器推断。

opt-in requirements

opt-in requirements现在不再需要额外的编译器配置。

在1.7.0之前,选择加入功能本身需要参数 opt-in=kotlin.RequiresOptIn以避免出现警告。m目前已经不再需要这个,但仍然可以使用编译器参数-opt-in以模块方式选择其他注释

绝对不可为空的类型

在Kotlin 1.7.0中,绝对不可为空的类型已被提升为稳定支持。在扩展通用Java类和接口时,它们提供了更好的互操作性。

你可以使用新语法 T & Any 将泛型类型参数标记为绝对不可为空

fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y

fun main() {
   // OK
   elvisLike<String>("", "").length
   // Error: 'null' cannot be a value of a non-null type
   elvisLike<String>("", null).length

   // OK
   elvisLike<String?>(null, "").length
   // Error: 'null' cannot be a value of a non-null type
   elvisLike<String?>(null, null).length
}

更多有关 绝对不可为空的类型的信息.

Kotlin/JVM

此版本为Kotlin/JVM编译器带来了性能改进和一个新的编译器选项。此外,对功能接口构造函数的可调用引用已变为稳定版。请注意,从1.7.0开始,Kotlin/JVM编译的默认目标版本现在是1.8。

编译器性能优化

Kotlin 1.7.0 引入了Kotlin/JVM编译器的性能改进。根据Kotlin团队的基准测试,与Kotlin 1.6.0相比,编译时间平均减少了10%。

新的编译器选项 -Xjdk-release

Kotlin 1.7.0 提供了一个新的编译器选项 -Xjdk-release。此选项类似于javac命令行中的--release选项。-Xjdk-release选项可以控制目标的字节码版本,并将类路径中JDK的API限制为指定的Java版本。例如,kotlinc -Xjdk-release=1.8, 将不允许引用java.lang.Module,即使依赖项中的JDK是9或更高版本。

此选项仅适用于 OpenJDK 发行版。

稳定支持对功能接口构造函数的引用调用

稳定支持对功能接口构造函数的引用调用。你可以了解如何从具有构造函数的接口迁移到使用可调用引用的函数式接口。

移除了 JVM 目标版本 1.6

Kotlin/JVM编译的默认目标版本已经变为1.8。 1.6已被删除。

请升级到JVM 1.8或更高版本。了解如何更新JVM目标版本。可以参考:

Kotlin/Native

Kotlin/JS正在对JS IR编译器后端进行进一步改进以及其他可以使您的开发体验更好的更新:

新 IR 后端的性能改进

此版本有一些重大更新,可以改善开发体验:

  • Kotlin/JS的增量编译性能得到了显着提升。构建JS项目所需的时间更少。在许多情况下,增量重建现在应该与legacy backend大致相当。

  • Kotlin/JS最终包需要更少的空间,与一些大型项目的legacy backend相比,生产包大小减少了20%。

  • 接口的类型检查已经提高了几个数量级。

  • Kotlin生成更高质量的JS代码

使用 IR 时成员名称的缩小

Kotlin/JS IR编译器现在使用其关于Kotlin类和函数关系的内部信息来应用更有效的压缩,缩短函数、属性和类的名称。这会缩小生成的捆绑应用程序。

在生产模式下构建Kotlin/JS应用程序时会自动应用这种类型的缩小,并且默认启用。要禁用成员名称缩小,可以使用-Xir-minimized-member-names编译器标志:

kotlin {
    js(IR) {
        compilations.all {
            compileKotlinTask.kotlinOptions.freeCompilerArgs += listOf("-Xir-minimized-member-names=false")
        }
    }
}

在 IR 后端通过 polyfill 支持旧版浏览器

Kotlin/JS的IR编译器后端现在包含与旧后端相同的polyfill。允许使用新编译器编译的代码在旧浏览器(不支持Kotlin标准库使用的ES2015中的所有方法。)中运行。只有项目实际使用的那些polyfill才会包含在最终的包中,这样可以最大限度地减少它们对包大小的潜在影响。

从 js 表达式动态加载 JavaScript 模块

使用JavaScript模块时,大多数应用程序使用静态导入,其使用包含在JavaScript的模块集成中。而Kotlin/JS 缺少在应用程序运行时动态加载JavaScript模块的机制。

从Kotlin 1.7.0开始,js中支持来自JavaScript的import语句,允许在运行时动态地将包引入应用程序:

val myPackage = js("import('my-package')")

为 JavaScript 测试运行器指定环境变量

要调整Node.js包解析或将外部信息传递给Node.js测试,现在可以指定JavaScript测试运行器使用的环境变量 定义环境变量,需要在构建脚本的testTask内使用environment()函数和键值对:

kotlin {
    js {
        nodejs {
            testTask {
                environment("key", "value")
            }
        }
    }
}

标准库

在 Kotlin 1.7.0中,标准库进行了一系列的改进。引入了新的特性,稳定了实验特性,并统一了对Native、JS 和JVM的命名捕获组的支持:

min() 和 max() 集合函数返回不可为空

在Kotlin 1.4.0 中, min()andmax()集合函数被重命名为minOrNull() 和 maxOrNull()。这些新名称更好地反映了它们的行为(如果接收者集合为空,则返回 null)。它还有助于使函数的行为与整个Kotlin集合API中使用的命名约定保持一致。

minBy(), maxBy(), minWith(), 和 maxWith() 也是如此。在Kotlin 1.4.0中都有它们的 OrNull() 同义词。此旧功能逐渐被弃用。

Kotlin 1.7.0 重新引入了原始函数名称,但返回类型不可为空。新的 min()、max()、minBy()、maxBy()、minWith() 和 maxWith() 函数现在严格返回集合元素或抛出异常。

fun main() {
    val numbers = listOf<Int>()
    println(numbers.maxOrNull()) // "null"
    println(numbers.max()) // "Exception in... Collection is empty."
}

特定索引处的正则表达式匹配

1.5.30中引入的 Regex.matchAt() 和 Regex.matchesAt() 函数现在已经成为稳定支持的。它们提供了一种方法来检查正则表达式是否在String或CharSequence中的特定位置完全匹配。

matchesAt() 检查匹配并返回布尔结果:

fun main() {
    val releaseText = "Kotlin 1.7.0 is on its way!"
    // 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:

fun main() {
    val releaseText = "Kotlin 1.7.0 is on its way!"
    val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()

    println(versionRegex.matchAt(releaseText, 0)) // "null"
    println(versionRegex.matchAt(releaseText, 7)?.value) // "1.7.0"
}

对以前的语言和 API 版本的扩展支持

为了支持库作者开发的库可以在各种先前的Kotlin 版本中使用,并解决主要Kotlin版本的频率增加,该版本扩展了对先前语言和API版本的支持。

在Kotlin 1.7.0 中,支持向前三个的语言和API版本,而不再是两个。这意味着Kotlin 1.7.0支持针对 Kotlin 版本低至1.4.0的库的开发。有关向后兼容性的更多信息,请参阅兼容模式。

通过反射访问注释

在1.6.0中首次引入的扩展功能现在是 Stable。此反射函数返回元素上给定类型的所有注释,包括单独应用和重复的注释。KAnnotatedElement.findAnnotations()

@Repeatable
annotation class Tag(val name: String)

@Tag("First Tag")
@Tag("Second Tag")
fun taggedFunction() {
    println("I'm a tagged function!")
}

fun main() {
    val x = ::taggedFunction
    val foo = x as KAnnotatedElement
    println(foo.findAnnotations<Tag>())
    // [@Tag(name=First Tag), @Tag(name=Second Tag)]
}

稳定版的深度递归函数

自Kotlin 1.4.0 以来,深度递归函数已作为实验性功能提供,现在它们在Kotlin 1.7.0 中是Stable 使用 DeepRecursiveFunction,可以定义一个将其堆栈保留在堆上的函数,而不是使用实际的调用堆栈。这允许运行非常深的递归计算。要调用一个深度递归函数,可以使用invoke。

在下列示例中,使用深度递归函数递归计算二叉树的深度。即使这个示例函数递归调用自身 100,000 次。也不会抛出StackOverflowError异常。

class Tree(val left: Tree?, val right: Tree?)

val calculateDepth = DeepRecursiveFunction<Tree?, Int> { t ->
    if (t == null) 0 else maxOf(
        callRecursive(t.left),
        callRecursive(t.right)
    ) + 1
}

fun main() {
    // Generate a tree with a depth of 100_000
    val deepTree = generateSequence(Tree(null, null)) { prev ->
        Tree(prev, null)
    }.take(100_000).last()

    println(calculateDepth(deepTree)) // 100000
}

考虑在递归深度超过 1000 次调用的代码中使用深度递归函数

基于默认时间源的内联类的时间标记

Kotlin 1.7.0通过将TimeSource.Monotonic返回的时间标记更改为内联值类来提高时间测量功能的性能。 这意味着调用诸如markNow()、elapsedNow()、measureTime() 和 measureTimedValue() 之类的函数不会为其TimeMark实例分配包装类。 尤其是在测量属于热路径的一段代码时,这有助于最大限度地减少测量对性能的影响:

@OptIn(ExperimentalTime::class)
fun main() {
    val mark = TimeSource.Monotonic.markNow() // Returned `TimeMark` is inline class
    val elapsedDuration = mark.elapsedNow()
}

仅当从中获取 TimeMark 的时间源静态已知为 TimeSource.Monotonic 时,此优化才可用。

Java Optionals 的新实验性扩展函数

Kotlin 1.7.0 带有新的便利函数,可简化在Java 中使用Optional类的工作。这些新函数可用于解包和转换 JVM上的可选对象,并有助于更简洁地使用Java API。

getOrNull()、getOrDefault() 和 getOrElse() 扩展函数允许获取Optional的值(如果存在)。否则,将分别获得默认值、null或函数返回的值。

val presentOptional = Optional.of("I'm here!")

println(presentOptional.getOrNull())
// "I'm here!"

val absentOptional = Optional.empty<String>()

println(absentOptional.getOrNull())
// null
println(absentOptional.getOrDefault("Nobody here!"))
// "Nobody here!"
println(absentOptional.getOrElse {
    println("Optional was absent!")
    "Default value!"
})
// "Optional was absent!"
// "Default value!"

toList()、toSet() 和 asSequence() 扩展函数将当前 Optional 的值转换为列表、集合或序列,否则返回空集合。toCollection() 扩展函数将Optional值附加到已经存在的目标集合。

val presentOptional = Optional.of("I'm here!")
val absentOptional = Optional.empty<String>()
println(presentOptional.toList() + "," + absentOptional.toList())
// ["I'm here!"], []
println(presentOptional.toSet() + "," + absentOptional.toSet())
// ["I'm here!"], []
val myCollection = mutableListOf<String>()
absentOptional.toCollection(myCollection)
println(myCollection)
// []
presentOptional.toCollection(myCollection)
println(myCollection)
// ["I'm here!"]
val list = listOf(presentOptional, absentOptional).flatMap { it.asSequence() }
println(list)
// ["I'm here!"]

这些扩展函数在Kotlin 1.7.0 中作为实验性引入。了解有关可选扩展的更多信息,请 移步

支持 JS 和 Native 中的命名捕获组

从Kotlin 1.7.0开始,命名捕获组不仅在JVM上支持,在JS和Native平台上也将支持。

要为捕获组命名,请在正则表达式中使用 (?<name>group) 语法。要获取组匹配的文本,请调用新引入的 MatchGroupCollection.get() 函数并传递组名称。

按名称检索匹配的组值

参考下面这个匹配城市坐标的例子。要获取与正则表达式匹配的组的集合,请使用组。比较按组的编号(索引)和使用值的名称检索组的内容:

fun main() {
    val regex = "\\b(?<city>[A-Za-z\\s]+),\\s(?<state>[A-Z]{2}):\\s(?<areaCode>[0-9]{3})\\b".toRegex()
    val input = "Coordinates: Austin, TX: 123"
    val match = regex.find(input)!!
    println(match.groups["city"]?.value) // "Austin" — by name
    println(match.groups[2]?.value) // "TX" — by number
}

命名反向引用

现在还可以在反向引用组时使用组名。反向引用匹配先前由捕获组匹配的相同文本。为此,请在正则表达式中使用 \k<name>语法:

fun backRef() {
    val regex = "(?<title>\\w+), yes \\k<title>".toRegex()
    val match = regex.find("Do you copy? Sir, yes Sir!")!!
    println(match.value) // "Sir, yes Sir"
    println(match.groups["title"]?.value) // "Sir"
}

替换表达式中的命名组

命名组引用可以与替换表达式一起使用。考虑用替换表达式替换输入中所有出现的指定正则表达式的replace()函数,以及仅交换第一个匹配项的replaceFirst()函数。

替换字符串中出现的${name}将替换为与具有指定名称的捕获组对应的子序列。可以按名称和索引比较组引用中的替换:

fun dateReplace() {
    val dateRegex = Regex("(?<dd>\\d{2})-(?<mm>\\d{2})-(?<yyyy>\\d{4})")
    val input = "Date of birth: 27-04-2022"
    println(dateRegex.replace(input, "\${yyyy}-\${mm}-\${dd}")) // "Date of birth: 2022-04-27" — by name
    println(dateRegex.replace(input, "\$3-\$2-\$1")) // "Date of birth: 2022-04-27" — by number
}

Gradle

此版本引入了新的构建报告、对Gradle插件变体的支持、kapt中的新统计信息等等:

增量编译的新方法

增量编译的新方法是实验性的。它可能随时被删除或更改。

在Kotlin 1.7.0中,Kotlin团队为跨模块更改重新设计了增量编译。现在,在依赖的非Kotlin模块中所做的更改也支持增量编译,并且它与Gradle构建缓存兼容 对编译避免的支持也得到了改进。

如果你构建缓存或经常在非otlin Gradle块中进行更改,你将会看到新方法所带来的最大好处。在kotlin-gradle-plugin 模块上对Kotlin项目的测试表明,缓存命中后的更改提高了80% 以上。

要尝试这种新方法,请在gradle.properties 中设置以下选项

kotlin.incremental.useClasspathSnapshot=true

增量编译的新方法目前仅适用于 Gradle 构建系统中的 JVM 后端。

Kotlin团队的计划是稳定这项技术并增加对其他后端(例如 JS)和构建系统的支持。

为 Kotlin 编译器任务构建报告

Kotlin 构建报告是实验性的。它们可能随时被删除或更改。

Kotlin 1.7.0 引入了帮助跟踪编译器性能的构建报告。报告包含不同编译阶段的持续时间,以及编译不能增量的原因。

当想要调查编译器任务的问题时,构建报告将会以下时机帮助你,例如:

  • 当Gradle构建花费太多时间并且你想了解性能不佳的根本原因时。
  • 当同一个项目的编译时间不同时,有时需要几秒钟,有时需要几分钟。

要启用构建报告,请在gradle.properties中声明构建报告输出的保存位置:

kotlin.build.report.output=file

以下值(及其组合)可用:

  • file 将构建报告保存在本地文件中
  • build_scan 将构建报告保存在[build scan](https://scans.gradle.com/).的custom values。
  • http 使用HTTP(S)发布构建报告。 POST 方法以JSON 格式发送指标。数据可能会因版本而异。可以在 Kotlin存储库中查看已发送数据的当前版本。

分析长时间运行编译的构建报告可以帮助解决两种常见情况:

  • 构建不是增量的。分析原因,解决根本问题。
  • 构建是增量的,但花费了太多时间。尝试重新组织源文件——拆分大文件,将单独的类保存在不同的文件中,重构大类,在不同的文件中声明顶级函数,等等

修改最低支持版本

从Kotlin 1.7.0开始,支持的最低Gradle 版本为 6.7.1。我们必须提高版本以支持Gradle插件变体和新的 Gradle API 将来,由于Gradle插件变体功能,Kotlin团队认为不应该经常提高支持的最低版本。

支持 Gradle 插件变体

Gradle 7.0 为 Gradle 插件作者引入了一项新功能——带有变体的插件。此功能可以更轻松地添加对新 Gradle 功能的支持,同时保持对低于7.1 的 Gradle 版本的兼容性。

使用 Gradle 插件变体,我们可以为不同的Gradle 版本提供不同的 Kotlin Gradle 插件变体。目标是在主要变体中支持基础Kotlin编译,这对应于Gradle的最旧支持版本。每个变体都将具有相应版本中Gradle功能的 实现。最新的变体将支持最广泛的Gradle 功能集。通过这种方法,我们可以扩展对功能有限的旧Gradle 版本的支持。

目前,Kotlin Gradle 插件只有两种变体:

  • main 对于 Gradle 版本 6.7.1 – 6.9.2
  • gradle70 对于 Gradle 7.0 及更高版本

要检查你的构建使用的哪个变体,启用--info 日志级别并在输出中查找以Using Kotlin Gradle plugin开头的字符串,例如,Using Kotlin Gradle plugin main variant。

以下是 Gradle 中变体选择的一些已知问题的解决方法:

  • pluginManagement 中的 ResolutionStrategy 不适用于具有多变量的插件

  • 当插件作为 buildSrc 通用依赖项添加时,插件变体被忽略

Kotlin Gradle 插件 API 中的更新

该版本Kotlin Gradle 插件API工件已获得多项改进:

  • Kotlin/JVM 和 Kotlin/kapt 任务具有用户可配置输入的新接口
  • 所有 Kotlin 插件都继承自一个新的 KotlinBasePlugin 接口。当想在应用任何 Kotlin Gradle 插件(JVM、JS、Multiplatform、Native 和其他平台)时触发一些配置操作时,请使用此接口:
project.plugins.withType<org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin>() {
    // Configure your action here
}
  • Kotlin团队已经为Android Gradle插件在其内部配置Kotlin编译奠定了基础,这意味着无需将Kotlin Android Gradle 插件添加到我们的构建中。

可通过插件 API 获得 sam-with-receiver 插件

sam-with-receiver 编译器插件现在可以通过 Gradle 插件 DSL 获得:

plugins {
    id("org.jetbrains.kotlin.plugin.sam.with.receiver") version "$kotlin_version"
}

编译任务的变化

编译任务在此版本中进行了很多更改:

  • Kotlin 编译任务不再继承 Gradle AbstractCompile任务。他们只继承DefaultTask
  • AbstractCompile任务具有sourceCompatibility和targetCompatibility输入。由于AbstractCompile不再继承任务,这些输入在 Kotlin 用户的脚本中不再可用。
  • 输入SourceTask.stableSources不再可用,应该使用sources输入。setSource(...)仍然可用的方法。
  • 所有编译任务都使用libraries输入作为编译所需的库列表。该KotlinCompile任务仍然具有已弃用的 Kotlin 属性classpath,将在未来的版本中删除。
  • 编译任务仍然实现PatternFilterable接口,允许过滤 Kotlin 源。删除了sourceFilesExtensions输入以支持使用PatternFilterable方法。
  • 已弃用的Gradle destinationDir: File输出已替换为destinationDirectory: DirectoryProperty输出。
  • Kotlin/Native任务AbstractNativeCompile现在继承了AbstractKotlinCompileTool基类。这是将 Kotlin/Native 构建工具集成到所有其他工具的第一步

为kapt中的每个注释处理器生成生成文件统计

kotlin-kapt Gradle 插件已经报告了每个处理器的性能统计信息。从Kotlin 1.7.0 开始,它还可以报告每个注释处理器生成的文件数量的统计信息。

这对于跟踪是否有未使用的注释处理器作为构建的一部分很有用。现在你可以使用生成的报告来查找触发不必要注释处理器的模块并更新模块以防止这种情况发生。

分两步以启用统计信息:

  • 在 build.gradle.kts 中将 showProcessorStats 标志设置为 true:
    kapt {
        showProcessorStats = true
    }
    
  • 在 gradle.properties 中将 kapt.verbose Gradle 属性设置为 true:
    kapt.verbose=true
    

还可以通过命令行选项 verbose 启用详细输出。

他的统计信息将出现在具有info级别的日志中。你将看到Annotation processor stats:。行后面是每个注释处理器的执行时间的统计信息。在这些行之后,将出现Generated files report: 行,然后是每个注释处理器生成的文件数量的统计信息。例如:

[INFO] Annotation processor stats:
[INFO] org.mapstruct.ap.MappingProcessor: total: 290 ms, init: 1 ms, 3 round(s): 289 ms, 0 ms, 0 ms
[INFO] Generated files report:
[INFO] org.mapstruct.ap.MappingProcessor: total sources: 2, sources per round: 2, 0, 0

弃用 kotlin.compiler.execution.strategy 系统属性

Kotlin 1.6.20 引入了用于定义 Kotlin 编译器执行策略的新属性。在 Kotlin 1.7.0 中,旧系统属性 kotlin.compiler.execution.strategy 开始进入弃用周期,以支持新属性。

使用 kotlin.compiler.execution.strategy 系统属性时,你将收到警告。此属性将在以后的版本中删除。 要保留旧行为,请将系统属性替换为同名的Gradle 属性。可以在 gradle.properties 中执行此操作,例如:

kotlin.compiler.execution.strategy=out-of-process

你还可以使用编译任务属性compilerExecutionStrategy。请在 Gradle 页面上 了解更多信息

删除不推荐使用的选项、方法和插件

移除 useExperimentalAnnotation 方法

在 Kotlin 1.7.0 中,Kotlin团队完成了useExperimentalAnnotation Gradle方法的弃用周期。使用 optIn() 来选择在模块中使用 API。

例如,如果你的 Gradle 模块是多平台的:

sourceSets {
    all {
        languageSettings.optIn("org.mylibrary.OptInAnnotation")
    }
}

删除不推荐使用的编译器选项

已经完成了几个编译器选项的弃用周期:

  • kotlinOptions.jdkHome 编译器选项在 1.5.30 中已弃用,并已在当前版本中删除。
  • 已弃用的noStdlib编译器选项也已删除。 Gradle插件使用 kotlin.stdlib.default.dependency=true 属性来控制是否存在 Kotlin 标准库。

编译器参数 -jdkHome 和 -no-stdlib 仍然可用

删除不推荐使用的插件

在 Kotlin 1.4.0 中,kotlin2js 和 kotlin-dce-plugin 插件已被弃用,并且在此版本中已被删除。使用新的 org.jetbrains.kotlin.js 插件代替 kotlin2js。使用新的org.jetbrains.kotlin.js 插件。正确配置 Kotlin/JS Gradle 插件后,Dead code elimination (DCE) 即可工作。

在 Kotlin 1.6.0 中,KotlinGradleSubplugin 类的弃用级别更改为错误。开发人员使用这个类来编写编译器插件。在此版本中,已删除此类。请改用 KotlinCompilerPluginSupportPlugin 类。

最佳实践是在整个项目中使用 1.7.0 及更高版本的 Kotlin 插件。

删除不推荐使用的协程 DSL 选项和属性

该版本删除了已弃用的 kotlin.experimental.coroutines Gradle DSL 选项和 gradle.properties 中使用的 kotlin.coroutines 属性。现在可以只使用 suspending functions 或将 kotlinx.coroutines 依赖项添加到构建脚本中。

删除工具链扩展方法中的类型转换

在 Kotlin 1.7.0 之前,在使用 Kotlin DSL 配置 Gradle 工具链时,必须将类型转换为 JavaToolchainSpec 类:

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

现在,可以省略这(this as JavaToolchainSpec) 部分:

kotlin {
    jvmToolchain {
        languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)
    }
}

如何迁移到 Kotlin 1.7.0

安装 Kotlin 1.7.0

IntelliJ IDEA 2022.1 和 Android Studio Chipmunk (212) 自动建议将 Kotlin 插件更新到 1.7.0。

对于 Intellij IDEA 2022.2 和 Android Studio Dolphin (213) 或 Android Studio Electric Eel (221),Kotlin 插件 1.7.0 将与即将到来的 Intellij IDEA 和 Android Studios 更新一起提供。

新的命令行编译器可在 GitHub 发布页面上下载。

使用 Kotlin 1.7.0 迁移现有项目或启动新项目

  • 要将现有项目迁移到 Kotlin 1.7.0,将 Kotlin 版本更改为 1.7.0 并重新导入 Gradle 或 Maven 项目。了解如何更新到 Kotlin 1.7.0。

  • 要使用 Kotlin 1.7.0 启动新项目,请更新 Kotlin 插件,并从File | New | Project 运行项目向导。

Kotlin 1.7.0 兼容性指南

Kotlin 1.7.0 是一个功能版本,因此可以带来与为该语言早期版本编写的代码不兼容的更改。在 Kotlin 1.7.0 的兼容性指南 中查看更改的详细列表。