Kotlin 1.7.0 的新特性

The IDE support for Kotlin 1.7.0 is available for IntelliJ IDEA 2021.2, 2021.3, and 2022.1.

发布于:2022-07-09

Kotlin 1.7.0 has been released. It unveils the Alpha version of the new Kotlin/JVM K2 compiler, stabilizes language features, and brings performance improvements for the JVM, JS, and Native platforms.

Here is a list of the major updates in this version:

You can also find a short overview of the changes in this video:

YouTube 视频:What’s new in Kotlin 1.7.0

用于 JVM 的新版 Kotlin K2 编译器进入 Alpha 阶段

This Kotlin release introduces the Alpha version of the new Kotlin K2 compiler. The new compiler aims to speed up the development of new language features, unify all of the platforms Kotlin supports, bring performance improvements, and provide an API for compiler extensions.

We’ve already published some detailed explanations of our new compiler and its benefits:

It’s important to point out that with the Alpha version of the new K2 compiler we were primarily focused on performance improvements, and it only works with JVM projects. It doesn’t support Kotlin/JS, Kotlin/Native, or other multi-platform projects, and none of compiler plugins, including kapt, work with it.

Our benchmarks show some outstanding results on our internal projects:

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

The KLOC/s performance numbers stand for the number of thousands of lines of code that the compiler processes per second.

Kotlin 1.7.0 - 图1

You can check out the performance boost on your JVM projects and compare it with the results of the old compiler. To enable the Kotlin K2 compiler, use the following compiler option:

  1. -Xuse-k2

Also, the K2 compiler includes a number of bugfixes. Please note that even issues with State: Open from this list are in fact fixed in K2.

The next Kotlin releases will improve the stability of the K2 compiler and provide more features, so stay tuned!

If you face any performance issues with the Kotlin K2 compiler, please report them to our issue tracker.

语言

Kotlin 1.7.0 introduces support for implementation by delegation and a new underscore operator for type arguments. It also stabilizes several language features introduced as previews in previous releases:

内联类的内联值可以通过委托来实现

If you want to create a lightweight wrapper for a value or class instance, it’s necessary to implement all interface methods by hand. Implementation by delegation solves this issue, but it did not work with inline classes before 1.7.0. This restriction has been removed, so you can now create lightweight wrappers that do not allocate memory in most cases.

  1. interface Bar {
  2. fun foo() = "foo"
  3. }
  4. @JvmInline
  5. value class BarWrapper(val bar: Bar): Bar by bar
  6. fun main() {
  7. val bw = BarWrapper(object: Bar {})
  8. println(bw.foo())
  9. }

下划线操作符用于类型参数

Kotlin 1.7.0 introduces an underscore operator, _, for type arguments. You can use it to automatically infer a type argument when other types are specified:

  1. abstract class SomeClass<T> {
  2. abstract fun execute(): T
  3. }
  4. class SomeImplementation : SomeClass<String>() {
  5. override fun execute(): String = "Test"
  6. }
  7. class OtherImplementation : SomeClass<Int>() {
  8. override fun execute(): Int = 42
  9. }
  10. object Runner {
  11. inline fun <reified S: SomeClass<T>, T> run(): T {
  12. return S::class.java.getDeclaredConstructor().newInstance().execute()
  13. }
  14. }
  15. fun main() {
  16. // T is inferred as String because SomeImplementation derives from SomeClass<String>
  17. val s = Runner.run<SomeImplementation, _>()
  18. assert(s == "Test")
  19. // T is inferred as Int because OtherImplementation derives from SomeClass<Int>
  20. val n = Runner.run<OtherImplementation, _>()
  21. assert(n == 42)
  22. }

You can use the underscore operator in any position in the variables list to infer a type argument.

Kotlin 1.7.0 - 图2

稳定版构建器推断

Builder inference is a special kind of type inference that is useful when calling generic builder functions. It helps the compiler infer the type arguments of a call using the type information about other calls inside its lambda argument.

Starting with 1.7.0, builder inference is automatically activated if a regular type inference cannot get enough information about a type without specifying the -Xenable-builder-inference compiler option, which was introduced in 1.6.0.

Learn how to write custom generic builders.

稳定版选择加入要求

Opt-in requirements are now Stable and do not require additional compiler configuration.

Before 1.7.0, the opt-in feature itself required the argument -opt-in=kotlin.RequiresOptIn to avoid a warning. It no longer requires this; however, you can still use the compiler argument -opt-in to opt-in for other annotations, module-wide.

稳定版绝对不可空类型

In Kotlin 1.7.0, definitely non-nullable types have been promoted to Stable. They provide better interoperability when extending generic Java classes and interfaces.

You can mark a generic type parameter as definitely non-nullable at the use site with the new syntax T & Any. The syntactic form comes from the notation for intersection types and is now limited to a type parameter with nullable upper bounds on the left side of & and a non-nullable Any on the right side:

  1. fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y
  2. fun main() {
  3. // OK
  4. elvisLike<String>("", "").length
  5. // Error: 'null' cannot be a value of a non-null type
  6. elvisLike<String>("", null).length
  7. // OK
  8. elvisLike<String?>(null, "").length
  9. // Error: 'null' cannot be a value of a non-null type
  10. elvisLike<String?>(null, null).length
  11. }

Learn more about definitely non-nullable types in this KEEP.

Kotlin/JVM

This release brings performance improvements for the Kotlin/JVM compiler and a new compiler option. Additionally, callable references to functional interface constructors have become Stable. Note that since 1.7.0, the default target version for Kotlin/JVM compilations is 1.8.

编译器性能优化

Kotlin 1.7.0 introduces performance improvements for the Kotlin/JVM compiler. According to our benchmarks, compilation time has been reduced by 10% on average compared to Kotlin 1.6.0. Projects with lots of usages of inline functions, for example, projects using kotlinx.html, will compile faster thanks to the improvements to the bytecode postprocessing.

新增编译器选项:-Xjdk-release

Kotlin 1.7.0 presents a new compiler option, -Xjdk-release. This option is similar to the javac’s command-line —release option. The -Xjdk-release option controls the target bytecode version and limits the API of the JDK in the classpath to the specified Java version. For example, kotlinc -Xjdk-release=1.8 won’t allow referencing java.lang.Module even if the JDK in the dependencies is version 9 or higher.

This option is not guaranteed to be effective for each JDK distribution.

Kotlin 1.7.0 - 图3

Please leave your feedback on this YouTrack ticket.

稳定版函数式接口构造函数的可调用引用

Callable references to functional interface constructors are now Stable. Learn how to migrate from an interface with a constructor function to a functional interface using callable references.

Please report any issues you find in YouTrack.

删除了 JVM 目标平台版本 1.6

The default target version for Kotlin/JVM compilations is 1.8. The 1.6 target has been removed.

Please migrate to JVM target 1.8 or above. Learn how to update the JVM target version for:

Kotlin/Native

Kotlin 1.7.0 includes changes to Objective-C and Swift interoperability and stabilizes features that were introduced in previous releases. It also brings performance improvements for the new memory manager along with other updates:

新版内存管理器的性能改进

The new Kotlin/Native memory manager is in Alpha. It may change incompatibly and require manual migration in the future. We would appreciate your feedback in YouTrack.

Kotlin 1.7.0 - 图4

The new memory manager is still in Alpha, but it is on its way to becoming Stable. This release delivers significant performance improvements for the new memory manager, especially in garbage collection (GC). In particular, concurrent implementation of the sweep phase, introduced in 1.6.20, is now enabled by default. This helps reduce the time the application is paused for GC. The new GC scheduler is better at choosing the GC frequency, especially for larger heaps.

Also, we’ve specifically optimized debug binaries, ensuring that the proper optimization level and link-time optimizations are used in the implementation code of the memory manager. This helped us improve execution time by roughly 30% for debug binaries on our benchmarks.

Try using the new memory manager in your projects to see how it works, and share your feedback with us in YouTrack.

与 JVM 及 JS IR 后端统一编译器插件 ABI

Starting with Kotlin 1.7.0, the Kotlin Multiplatform Gradle plugin uses the embeddable compiler jar for Kotlin/Native by default. This feature was announced in 1.6.0 as Experimental, and now it’s Stable and ready to use.

This improvement is very handy for library authors, as it improves the compiler plugin development experience. Before this release, you had to provide separate artifacts for Kotlin/Native, but now you can use the same compiler plugin artifacts for Native and other supported platforms.

This feature might require plugin developers to take migration steps for their existing plugins.

Learn how to prepare your plugin for the update in this YouTrack issue.

Kotlin 1.7.0 - 图5

支持独立的 Android 可执行文件

Kotlin 1.7.0 provides full support for generating standard executables for Android Native targets. It was introduced in 1.6.20, and now it’s enabled by default.

If you want to roll back to the previous behavior when Kotlin/Native generated shared libraries, use the following setting:

  1. binaryOptions["androidProgramType"] = "nativeActivity"

与 Swift async/await 互操作:返回 Void 而不是 KotlinUnit

Kotlin suspend functions now return the Void type instead of KotlinUnit in Swift. This is the result of the improved interop with Swift’s async/await. This feature was introduced in 1.6.20, and this release enables this behavior by default.

You don’t need to use the kotlin.native.binary.unitSuspendFunctionObjCExport=proper property anymore to return the proper type for such functions.

通过 Objective-C 桥接禁止未声明的异常

When you call Kotlin code from Swift/Objective-C code (or vice versa) and this code throws an exception, it should be handled by the code where the exception occurred, unless you specifically allowed the forwarding of exceptions between languages with proper conversion (for example, using the @Throws annotation).

Previously, Kotlin had another unintended behavior where undeclared exceptions could “leak” from one language to another in some cases. Kotlin 1.7.0 fixes that issue, and now such cases lead to program termination.

So, for example, if you have a { throw Exception() } lambda in Kotlin and call it from Swift, in Kotlin 1.7.0 it will terminate as soon as the exception reaches the Swift code. In previous Kotlin versions, such an exception could leak to the Swift code.

The @Throws annotation continues to work as before.

改进了 CocoaPods 集成

Starting with Kotlin 1.7.0, you no longer need to install the cocoapods-generate plugin if you want to integrate CocoaPods in your projects.

Previously, you needed to install both the CocoaPods dependency manager and the cocoapods-generate plugin to use CocoaPods, for example, to handle iOS dependencies in Kotlin Multiplatform Mobile projects.

Now setting up the CocoaPods integration is easier, and we’ve resolved the issue when cocoapods-generate couldn’t be installed on Ruby 3 and later. Now the newest Ruby versions that work better on Apple M1 are also supported.

See how to set up the initial CocoaPods integration.

覆盖了 Kotlin/Native 编译器下载 URL

Starting with Kotlin 1.7.0, you can customize the download URL for the Kotlin/Native compiler. This is useful when external links on the CI are forbidden.

To override the default base URL https://download.jetbrains.com/kotlin/native/builds, use the following Gradle property:

  1. kotlin.native.distribution.baseDownloadUrl=https://example.com

The downloader will append the native version and target OS to this base URL to ensure it downloads the actual compiler distribution.

Kotlin 1.7.0 - 图6

Kotlin/JS

Kotlin/JS is receiving further improvements to the JS IR compiler backend along with other updates that can make your development experience better:

新版 IR 后端的性能改进

This release has some major updates that should improve your development experience:

  • Incremental compilation performance of Kotlin/JS has been significantly improved. It takes less time to build your JS projects. Incremental rebuilds should now be roughly on par with the legacy backend in many cases now.
  • The Kotlin/JS final bundle requires less space, as we have significantly reduced the size of the final artifacts. We’ve measured up to a 20% reduction in the production bundle size compared to the legacy backend for some large projects.
  • Type checking for interfaces has been improved by orders of magnitude.
  • Kotlin generates higher-quality JS code

使用 IR 时的成员名缩短

The Kotlin/JS IR compiler now uses its internal information about the relationships of your Kotlin classes and functions to apply more efficient minification, shortening the names of functions, properties, and classes. This shrinks the resulting bundled applications.

This type of minification is automatically applied when you build your Kotlin/JS application in production mode and is enabled by default. To disable member name minification, use the -Xir-minimized-member-names compiler flag:

  1. kotlin {
  2. js(IR) {
  3. compilations.all {
  4. compileKotlinTask.kotlinOptions.freeCompilerArgs += listOf("-Xir-minimized-member-names=false")
  5. }
  6. }
  7. }

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

The IR compiler backend for Kotlin/JS now includes the same polyfills as the legacy backend. This allows code compiled with the new compiler to run in older browsers that do not support all the methods from ES2015 used by the Kotlin standard library. Only those polyfills actually used by the project are included in the final bundle, which minimizes their potential impact on the bundle size.

This feature is enabled by default when using the IR compiler, and you don’t need to configure it.

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

When working with the JavaScript modules, most applications use static imports, whose use is covered with the JavaScript module integration. However, Kotlin/JS was missing a mechanism to load JavaScript modules dynamically at runtime in your applications.

Starting with Kotlin 1.7.0, the import statement from JavaScript is supported in js blocks, allowing you to dynamically bring packages into your application at runtime:

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

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

To tune Node.js package resolution or pass external information to Node.js tests, you can now specify environment variables used by the JavaScript test runners. To define an environment variable, use the environment() function with a key-value pair inside the testTask block in your build script:

  1. kotlin {
  2. js {
  3. nodejs {
  4. testTask {
  5. environment("key", "value")
  6. }
  7. }
  8. }
  9. }

标准库

In Kotlin 1.7.0, the standard library has received a range of changes and improvements. They introduce new features, stabilize experimental ones, and unify support for named capturing groups for Native, JS, and the JVM:

集合函数 min() 与 max() 返回不可空值

In Kotlin 1.4.0, we renamed the min() and max() collection functions to minOrNull() and maxOrNull(). These new names better reflect their behavior – returning null if the receiver collection is empty. It also helped align the functions’ behavior with naming conventions used throughout the Kotlin collections API.

The same was true of minBy(), maxBy(), minWith(), and maxWith(), which all got their *OrNull() synonyms in Kotlin 1.4.0. Older functions affected by this change were gradually deprecated.

Kotlin 1.7.0 reintroduces the original function names, but with a non-nullable return type. The new min(), max() , minBy(), maxBy(), minWith(), and maxWith() functions now strictly return the collection element or throw an exception.

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

指定索引位置的正则表达式匹配

The Regex.matchAt() and Regex.matchesAt() functions, introduced in 1.5.30, are now Stable. They provide a way to check whether a regular expression has an exact match at a particular position in a String or CharSequence.

matchesAt() checks for a match and returns a boolean result:

  1. fun main() {
  2. val releaseText = "Kotlin 1.7.0 is on its way!"
  3. // regular expression: one digit, dot, one digit, dot, one or more digits
  4. val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()
  5. println(versionRegex.matchesAt(releaseText, 0)) // "false"
  6. println(versionRegex.matchesAt(releaseText, 7)) // "true"
  7. }

matchAt() returns the match if it’s found, or null if it isn’t:

  1. fun main() {
  2. val releaseText = "Kotlin 1.7.0 is on its way!"
  3. val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()
  4. println(versionRegex.matchAt(releaseText, 0)) // "null"
  5. println(versionRegex.matchAt(releaseText, 7)?.value) // "1.7.0"
  6. }

We’d be grateful for your feedback on this YouTrack issue.

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

To support library authors developing libraries that are meant to be consumable in a wide range of previous Kotlin versions, and to address the increased frequency of major Kotlin releases, we have extended our support for previous language and API versions.

With Kotlin 1.7.0, we’re supporting three previous language and API versions rather than two. This means Kotlin 1.7.0 supports the development of libraries targeting Kotlin versions down to 1.4.0. For more information on backward compatibility, see Compatibility modes.

通过反射访问注解

The KAnnotatedElement.[findAnnotations()](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect.full/find-annotations.html) extension function, which was first introduced in 1.6.0, is now Stable. This reflection function returns all annotations of a given type on an element, including individually applied and repeated annotations.

  1. @Repeatable
  2. annotation class Tag(val name: String)
  3. @Tag("First Tag")
  4. @Tag("Second Tag")
  5. fun taggedFunction() {
  6. println("I'm a tagged function!")
  7. }
  8. fun main() {
  9. val x = ::taggedFunction
  10. val foo = x as KAnnotatedElement
  11. println(foo.findAnnotations<Tag>()) // [@Tag(name=First Tag), @Tag(name=Second Tag)]
  12. }

稳定版深递归函数

Deep recursive functions have been available as an experimental feature since Kotlin 1.4.0, and they are now Stable in Kotlin 1.7.0. Using DeepRecursiveFunction, you can define a function that keeps its stack on the heap instead of using the actual call stack. This allows you to run very deep recursive computations. To call a deep recursive function, invoke it.

In this example, a deep recursive function is used to calculate the depth of a binary tree recursively. Even though this sample function calls itself recursively 100,000 times, no StackOverflowError is thrown:

  1. class Tree(val left: Tree?, val right: Tree?)
  2. val calculateDepth = DeepRecursiveFunction<Tree?, Int> { t ->
  3. if (t == null) 0 else maxOf(
  4. callRecursive(t.left),
  5. callRecursive(t.right)
  6. ) + 1
  7. }
  8. fun main() {
  9. // Generate a tree with a depth of 100_000
  10. val deepTree = generateSequence(Tree(null, null)) { prev ->
  11. Tree(prev, null)
  12. }.take(100_000).last()
  13. println(calculateDepth(deepTree)) // 100000
  14. }

Consider using deep recursive functions in your code where your recursion depth exceeds 1000 calls.

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

Kotlin 1.7.0 improves the performance of time measurement functionality by changing the time marks returned by TimeSource.Monotonic into inline value classes. This means that calling functions like markNow(), elapsedNow() , measureTime(), and measureTimedValue() doesn’t allocate wrapper classes for their TimeMark instances. Especially when measuring a piece of code that is part of a hot path, this can help minimize the performance impact of the measurement:

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

This optimization is only available if the time source from which the TimeMark is obtained is statically known to be TimeSource.Monotonic.

Kotlin 1.7.0 - 图7

Java Optional 的新实验性扩展函数

Kotlin 1.7.0 comes with new convenience functions that simplify working with Optional classes in Java. These new functions can be used to unwrap and convert optional objects on the JVM and help make working with Java APIs more concise.

The getOrNull(), getOrDefault(), and getOrElse() extension functions allow you to get the value of an Optional if it’s present. Otherwise, you get null, a default value, or a value returned by a function, respectively:

  1. val presentOptional = Optional.of("I'm here!")
  2. println(presentOptional.getOrNull())
  3. // "I'm here!"
  4. val absentOptional = Optional.empty<String>()
  5. println(absentOptional.getOrNull())
  6. // null
  7. println(absentOptional.getOrDefault("Nobody here!"))
  8. // "Nobody here!"
  9. println(absentOptional.getOrElse {
  10. println("Optional was absent!")
  11. "Default value!"
  12. })
  13. // "Optional was absent!"
  14. // "Default value!"

The toList(), toSet(), and asSequence() extension functions convert the value of a present Optional to a list, set, or sequence, or return an empty collection otherwise. The toCollection() extension function appends the Optional value to an already existing destination collection:

  1. val presentOptional = Optional.of("I'm here!")
  2. val absentOptional = Optional.empty<String>()
  3. println(presentOptional.toList() + "," + absentOptional.toList())
  4. // ["I'm here!"], []
  5. println(presentOptional.toSet() + "," + absentOptional.toSet())
  6. // ["I'm here!"], []
  7. val myCollection = mutableListOf<String>()
  8. absentOptional.toCollection(myCollection)
  9. println(myCollection)
  10. // []
  11. presentOptional.toCollection(myCollection)
  12. println(myCollection)
  13. // ["I'm here!"]
  14. val list = listOf(presentOptional, absentOptional).flatMap { it.asSequence() }
  15. println(list)
  16. // ["I'm here!"]

These extension functions are being introduced as Experimental in Kotlin 1.7.0. You can learn more about Optional extensions in this KEEP. As always, we welcome your feedback in the Kotlin issue tracker.

在 JS 与 Native 平台支持命名捕获

Starting with Kotlin 1.7.0, named capturing groups are supported not only on the JVM, but on the JS and Native platforms as well.

To give a name to a capturing group, use the (?<name>group) syntax in your regular expression. To get the text matched by a group, call the newly introduced MatchGroupCollection.get() function and pass the group name.

按名称取回匹配分组的值

Consider this example for matching city coordinates. To get a collection of groups matched by the regular expression, use groups. Compare retrieving a group’s contents by its number (index) and by its name using value:

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

命名反向引用

You can now also use group names when backreferencing groups. Backreferences match the same text that was previously matched by a capturing group. For this, use the \k<name> syntax in your regular expression:

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

替换表达式中的命名分组

Named group references can be used with replacement expressions. Consider the replace() function that substitutes all occurrences of the specified regular expression in the input with a replacement expression, and the replaceFirst() function that swaps the first match only.

Occurrences of ${name} in the replacement string are substituted with the subsequences corresponding to the captured groups with the specified name. You can compare replacements in group references by name and index:

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

Gradle

This release introduces new build reports, support for Gradle plugin variants, new statistics in kapt, and a lot more:

增量编译的新方式

The new approach to incremental compilation is Experimental. It may be dropped or changed at any time. Opt-in is required (see the details below). We encourage you to use it only for evaluation purposes, and we would appreciate your feedback in YouTrack.

Kotlin 1.7.0 - 图8

In Kotlin 1.7.0, we’ve reworked incremental compilation for cross-module changes. Now incremental compilation is also supported for changes made inside dependent non-Kotlin modules, and it is compatible with the Gradle build cache. Support for compilation avoidance has also been improved.

We expect you’ll see the most significant benefit of the new approach if you use the build cache or frequently make changes in non-Kotlin Gradle modules. Our tests for the Kotlin project on the kotlin-gradle-plugin module show an improvement of greater than 80% for the changes after the cache hit.

To try this new approach, set the following option in your gradle.properties:

  1. kotlin.incremental.useClasspathSnapshot=true

The new approach to incremental compilation is currently available for the JVM backend in the Gradle build system only.

Kotlin 1.7.0 - 图9

Learn how the new approach to incremental compilation is implemented under the hood in this blog post.

Our plan is to stabilize this technology and add support for other backends (JS, for instance) and build systems. We’d appreciate your reports in YouTrack about any issues or strange behavior you encounter in this compilation scheme. Thank you!

The Kotlin team is very grateful to Ivan Gavrilovic, Hung Nguyen, Cédric Champeau, and other external contributors for their help.

Kotlin 编译器任务的构建报告

Kotlin build reports are Experimental. They may be dropped or changed at any time. Opt-in is required (see details below). Use them only for evaluation purposes. We appreciate your feedback on them in YouTrack.

Kotlin 1.7.0 - 图10

Kotlin 1.7.0 introduces build reports that help track compiler performance. Reports contain the durations of different compilation phases and reasons why compilation couldn’t be incremental.

Build reports come in handy when you want to investigate issues with compiler tasks, for example:

  • When the Gradle build takes too much time and you want to understand the root cause of the poor performance.
  • When the compilation time for the same project differs, sometimes taking seconds, sometimes taking minutes.

To enable build reports, declare where to save the build report output in gradle.properties:

  1. kotlin.build.report.output=file

The following values (and their combinations) are available:

  • file saves build reports in a local file.
  • build_scan saves build reports in the custom values section of the build scan.

    The Gradle Enterprise plugin limits the number of custom values and their length. In big projects, some values could be lost.

    Kotlin 1.7.0 - 图11

  • http posts build reports using HTTP(S). The POST method sends metrics in the JSON format. Data may change from version to version. You can see the current version of the sent data in the Kotlin repository.

There are two common cases that analyzing build reports for long-running compilations can help you resolve:

  • The build wasn’t incremental. Analyze the reasons and fix underlying problems.
  • The build was incremental, but took too much time. Try to reorganize source files — split big files, save separate classes in different files, refactor large classes, declare top-level functions in different files, and so on.

Learn more about new build reports in this blog post.

You are welcome to try using build reports in your infrastructure. If you have any feedback, encounter any issues, or want to suggest improvements, please don’t hesitate to report them in our issue tracker. Thank you!

最低支持版本升级

Starting with Kotlin 1.7.0, the minimum supported Gradle version is 6.7.1. We had to raise the version to support Gradle plugin variants and the new Gradle API. In the future, we should not have to raise the minimum supported version as often, thanks to the Gradle plugin variants feature.

Also, the minimal supported Android Gradle plugin version is now 3.6.4.

支持 Gradle 插件变体

Gradle 7.0 introduced a new feature for Gradle plugin authors — plugins with variants. This feature makes it easier to add support for new Gradle features while maintaining compatibility for Gradle versions below 7.1. Learn more about variant selection in Gradle.

With Gradle plugin variants, we can ship different Kotlin Gradle plugin variants for different Gradle versions. The goal is to support the base Kotlin compilation in the main variant, which corresponds to the oldest supported versions of Gradle. Each variant will have implementations for Gradle features from a corresponding release. The latest variant will support the widest Gradle feature set. With this approach, we can extend support for older Gradle versions with limited functionality.

Currently, there are only two variants of the Kotlin Gradle plugin:

  • main for Gradle versions 6.7.1–6.9.3
  • gradle70 for Gradle versions 7.0 and higher

In future Kotlin releases, we may add more.

To check which variant your build uses, enable the --info log level and find a string in the output starting with Using Kotlin Gradle plugin, for example, Using Kotlin Gradle plugin main variant.

Here are workarounds for some known issues with variant selection in Gradle:

Kotlin 1.7.0 - 图12

Leave your feedback on this YouTrack ticket.

Kotlin Gradle 插件 API 的更新

The Kotlin Gradle plugin API artifact has received several improvements:

  • There are new interfaces for Kotlin/JVM and Kotlin/kapt tasks with user-configurable inputs.
  • There is a new KotlinBasePlugin interface that all Kotlin plugins inherit from. Use this interface when you want to trigger some configuration action whenever any Kotlin Gradle plugin (JVM, JS, Multiplatform, Native, and other platforms) is applied:

    1. project.plugins.withType<org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin>() {
    2. // Configure your action here
    3. }

    You can leave your feedback about the KotlinBasePlugin in this YouTrack ticket.

  • We’ve laid the groundwork for the Android Gradle plugin to configure Kotlin compilation within itself, meaning you won’t need to add the Kotlin Android Gradle plugin to your build. Follow Android Gradle Plugin release announcements to learn about the added support and try it out!

通过插件 API 提供了 sam-with-receiver 插件

The sam-with-receiver compiler plugin is now available via the Gradle plugins DSL:

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

编译任务的变更

Compile tasks have received lots of changes in this release:

  • Kotlin compile tasks no longer inherit the Gradle AbstractCompile task. They inherit only the DefaultTask.
  • The AbstractCompile task has the sourceCompatibility and targetCompatibility inputs. Since the AbstractCompile task is no longer inherited, these inputs are no longer available in Kotlin users’ scripts.
  • The SourceTask.stableSources input is no longer available, and you should use the sources input. setSource(...) methods are still available.
  • All compile tasks now use the libraries input for a list of libraries required for compilation. The KotlinCompile task still has the deprecated Kotlin property classpath, which will be removed in future releases.
  • Compile tasks still implement the PatternFilterable interface, which allows the filtering of Kotlin sources. The sourceFilesExtensions input was removed in favor of using PatternFilterable methods.
  • The deprecated Gradle destinationDir: File output was replaced with the destinationDirectory: DirectoryProperty output.
  • The Kotlin/Native AbstractNativeCompile task now inherits the AbstractKotlinCompileTool base class. This is an initial step toward integrating Kotlin/Native build tools into all the other tools.

Please leave your feedback in this YouTrack ticket.

kapt 中每个注解处理器生成文件的统计信息

The kotlin-kapt Gradle plugin already reports performance statistics for each processor. Starting with Kotlin 1.7.0, it can also report statistics on the number of generated files for each annotation processor.

This is useful to track if there are unused annotation processors as a part of the build. You can use the generated report to find modules that trigger unnecessary annotation processors and update the modules to prevent that.

Enable the statistics in two steps:

  • Set the showProcessorStats flag to true in your build.gradle.kts:

    1. kapt {
    2. showProcessorStats = true
    3. }
  • Set the kapt.verbose Gradle property to true in your gradle.properties:

    1. kapt.verbose=true

You can also enable verbose output via the command line option verbose.

Kotlin 1.7.0 - 图13

The statistics will appear in the logs with the info level. You’ll see the Annotation processor stats: line followed by statistics on the execution time of each annotation processor. After these lines, there will be the Generated files report: line followed by statistics on the number of generated files for each annotation processor. For example:

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

Please leave your feedback in this YouTrack ticket.

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

Kotlin 1.6.20 introduced new properties for defining a Kotlin compiler execution strategy. In Kotlin 1.7.0, a deprecation cycle has started for the old system property kotlin.compiler.execution.strategy in favor of the new properties.

When using the kotlin.compiler.execution.strategy system property, you’ll receive a warning. This property will be deleted in future releases. To preserve the old behavior, replace the system property with the Gradle property of the same name. You can do this in gradle.properties, for example:

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

You can also use the compile task property compilerExecutionStrategy. Learn more about this on the Gradle page.

删除弃用的选项、方法与插件

删除 useExperimentalAnnotation 方法

In Kotlin 1.7.0, we completed the deprecation cycle for the useExperimentalAnnotation Gradle method. Use optIn() instead to opt in to using an API in a module.

For example, if your Gradle module is multiplatform:

  1. sourceSets {
  2. all {
  3. languageSettings.optIn("org.mylibrary.OptInAnnotation")
  4. }
  5. }

Learn more about opt-in requirements in Kotlin.

删除弃用的编译器选项

We’ve completed the deprecation cycle for several compiler options:

  • The kotlinOptions.jdkHome compiler option was deprecated in 1.5.30 and has been removed in the current release. Gradle builds now fail if they contain this option. We encourage you to use Java toolchains, which have been supported since Kotlin 1.5.30.
  • The deprecated noStdlib compiler option has also been removed. The Gradle plugin uses the kotlin.stdlib.default.dependency=true property to control whether the Kotlin standard library is present.

The compiler arguments -jdkHome and -no-stdlib are still available.

Kotlin 1.7.0 - 图14

删除弃用的插件

In Kotlin 1.4.0, the kotlin2js and kotlin-dce-plugin plugins were deprecated, and they have been removed in this release. Instead of kotlin2js, use the new org.jetbrains.kotlin.js plugin. Dead code elimination (DCE) works when the Kotlin/JS Gradle plugin is properly configured.

In Kotlin 1.6.0, we changed the deprecation level of the KotlinGradleSubplugin class to ERROR. Developers used this class for writing compiler plugins. In this release, this class has been removed. Use the KotlinCompilerPluginSupportPlugin class instead.

The best practice is to use Kotlin plugins with versions 1.7.0 and higher throughout your project.

Kotlin 1.7.0 - 图15

删除弃用的协程 DSL 选项与属性

We removed the deprecated kotlin.experimental.coroutines Gradle DSL option and the kotlin.coroutines property used in gradle.properties. Now you can just use suspending functions or add the kotlinx.coroutines dependency to your build script.

Learn more about coroutines in the Coroutines guide.

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

Before Kotlin 1.7.0, you had to do the type cast into the JavaToolchainSpec class when configuring the Gradle toolchain with Kotlin DSL:

  1. kotlin {
  2. jvmToolchain {
  3. (this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)
  4. }
  5. }

Now, you can omit the (this as JavaToolchainSpec) part:

  1. kotlin {
  2. jvmToolchain {
  3. languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)
  4. }
  5. }

迁移到 Kotlin 1.7.0

安装 Kotlin 1.7.0

IntelliJ IDEA 2022.1 and Android Studio Chipmunk (212) automatically suggest updating the Kotlin plugin to 1.7.0.

For IntelliJ IDEA 2022.2, and Android Studio Dolphin (213) or Android Studio Electric Eel (221), the Kotlin plugin 1.7.0 will be delivered with upcoming IntelliJ IDEA and Android Studios updates.

Kotlin 1.7.0 - 图16

The new command-line compiler is available for download on the GitHub release page.

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

  • To migrate existing projects to Kotlin 1.7.0, change the Kotlin version to 1.7.0 and reimport your Gradle or Maven project. Learn how to update to Kotlin 1.7.0.

  • To start a new project with Kotlin 1.7.0, update the Kotlin plugin and run the Project Wizard from File | New | Project.

Kotlin 1.7.0 的兼容性指南

Kotlin 1.7.0 is a feature release and can, therefore, bring changes that are incompatible with your code written for earlier versions of the language. Find the detailed list of such changes in the Compatibility guide for Kotlin 1.7.0.