Kotlin 1.8.0 的新特性

发布于:2022-12-28

The Kotlin 1.8.0 release is out and here are some of its biggest highlights:

IDE 支持

The Kotlin plugin that supports 1.8.0 is available for:

IDE所支持版本
IntelliJ IDEA2021.3、 2022.1、 2022.2
Android StudioElectric Eel(221)、 Flamingo(222)

You can update your projects to Kotlin 1.8.0 in IntelliJ IDEA 2022.3 without updating the IDE plugin.

To migrate existing projects to Kotlin 1.8.0 in IntelliJ IDEA 2022.3, change the Kotlin version to 1.8.0 and reimport your Gradle or Maven project.

Kotlin 1.8.0 - 图1

Kotlin/JVM

Starting with version 1.8.0, the compiler can generate classes with a bytecode version corresponding to JVM 19. The new language version also includes:

不生成 TYPE_USE 与 TYPE_PARAMETER 注解目标的能力

If a Kotlin annotation has TYPE among its Kotlin targets, the annotation maps to java.lang.annotation.ElementType.TYPE_USE in its list of Java annotation targets. This is just like how the TYPE_PARAMETER Kotlin target maps to the java.lang.annotation.ElementType.TYPE_PARAMETER Java target. This is an issue for Android clients with API levels less than 26, which don’t have these targets in the API.

Starting with Kotlin 1.8.0, you can use the new compiler option -Xno-new-java-annotation-targets to avoid generating the TYPE_USE and TYPE_PARAMETER annotation targets.

用于禁用优化的新增编译器选项

Kotlin 1.8.0 adds a new -Xdebug compiler option, which disables optimizations for a better debugging experience. For now, the option disables the “was optimized out” feature for coroutines. In the future, after we add more optimizations, this option will disable them, too.

The “was optimized out” feature optimizes variables when you use suspend functions. However, it is difficult to debug code with optimized variables because you don’t see their values.

Never use this option in production: Disabling this feature via -Xdebug can cause memory leaks.

Kotlin 1.8.0 - 图2

删除旧版后端

In Kotlin 1.5.0, we announced that the IR-based backend became Stable. That meant that the old backend from Kotlin 1.4.* was deprecated. In Kotlin 1.8.0, we’ve removed the old backend completely. By extension, we’ve removed the compiler option -Xuse-old-backend and the Gradle useOldBackend option.

支持 Lombok 的 @Builder 注解

The community has added so many votes for the Kotlin Lombok: Support generated builders (@Builder) YouTrack issue that we just had to support the @Builder annotation.

We don’t yet have plans to support the @SuperBuilder or @Tolerate annotations, but we’ll reconsider if enough people vote for the @SuperBuilder and @Tolerate issues.

Learn how to configure the Lombok compiler plugin.

Kotlin/Native

Kotlin 1.8.0 includes changes to Objective-C and Swift interoperability, support for Xcode 14.1, and improvements to the CocoaPods Gradle plugin:

支持 Xcode 14.1

The Kotlin/Native compiler now supports the latest stable Xcode version, 14.1. The compatibility improvements include the following changes:

  • There’s a new watchosDeviceArm64 preset for the watchOS target that supports Apple watchOS on ARM64 platforms.
  • The Kotlin CocoaPods Gradle plugin no longer has bitcode embedding for Apple frameworks by default.
  • Platform libraries were updated to reflect the changes to Objective-C frameworks for Apple targets.

改进了 Objective-C/Swift 互操作性

To make Kotlin more interoperable with Objective-C and Swift, three new annotations were added:

  • @ObjCName allows you to specify a more idiomatic name in Swift or Objective-C, instead of renaming the Kotlin declaration.

    The annotation instructs the Kotlin compiler to use a custom Objective-C and Swift name for this class, property, parameter, or function:

    1. @ObjCName(swiftName = "MySwiftArray")
    2. class MyKotlinArray {
    3. @ObjCName("index")
    4. fun indexOf(@ObjCName("of") element: String): Int = TODO()
    5. }
    6. // Usage with the ObjCName annotations
    7. let array = MySwiftArray()
    8. let index = array.index(of: "element")
  • @HiddenFromObjC allows you to hide a Kotlin declaration from Objective-C.

    The annotation instructs the Kotlin compiler not to export a function or property to Objective-C and, consequently, Swift. This can make your Kotlin code more Objective-C/Swift-friendly.

  • @ShouldRefineInSwift is useful for replacing a Kotlin declaration with a wrapper written in Swift.

    The annotation instructs the Kotlin compiler to mark a function or property as swift_private in the generated Objective-C API. Such declarations get the __ prefix, which makes them invisible to Swift code.

    You can still use these declarations in your Swift code to create a Swift-friendly API, but they won’t be suggested by Xcode’s autocompletion, for example.

    For more information on refining Objective-C declarations in Swift, see the official Apple documentation.

The new annotations require opt-in.

Kotlin 1.8.0 - 图3

The Kotlin team is very grateful to Rick Clephas for implementing these annotations.

CocoaPods Gradle 插件中默认动态 framework

Starting with Kotlin 1.8.0, Kotlin frameworks registered by the CocoaPods Gradle plugin are linked dynamically by default. The previous static implementation was inconsistent with the behavior of the Kotlin Gradle plugin.

  1. kotlin {
  2. cocoapods {
  3. framework {
  4. baseName = "MyFramework"
  5. isStatic = false // Now dynamic by default
  6. }
  7. }
  8. }

If you have an existing project with a static linking type and you upgrade to Kotlin 1.8.0 (or change the linking type explicitly), you may encounter an error with the project’s execution. To fix it, close your Xcode project and run pod install in the Podfile directory.

For more information, see the CocoaPods Gradle plugin DSL reference.

Kotlin 多平台:新版 Android 源代码集布局

Kotlin 1.8.0 introduces a new Android source set layout that replaces the previous naming schema for directories, which is confusing in multiple ways.

Consider an example of two androidTest directories created in the current layout. One is for KotlinSourceSets and the other is for AndroidSourceSets:

  • They have different semantics: Kotlin’s androidTest belongs to the unitTest type, whereas Android’s belongs to the integrationTest type.
  • They create a confusing SourceDirectories layout, as src/androidTest/kotlin has a UnitTest and src/androidTest/java has an InstrumentedTest.
  • Both KotlinSourceSets and AndroidSourceSets use a similar naming schema for Gradle configurations, so the resulting configurations of androidTest for both Kotlin’s and Android’s source sets are the same: androidTestImplementation, androidTestApi, androidTestRuntimeOnly, and androidTestCompileOnly.

To address these and other existing issues, we’ve introduced a new Android source set layout. Here are some of the key differences between the two layouts:

KotlinSourceSet 命名模式

当前源代码集布局新版源代码集布局
targetName + AndroidSourceSet.nametargetName + AndroidVariantType

{AndroidSourceSet.name} maps to {KotlinSourceSet.name} as follows:

当前源代码集布局新版源代码集布局
mainandroidMainandroidMain
testandroidTestandroidUnitTest
androidTestandroidAndroidTestandroidInstrumentedTest

SourceDirectories

当前源代码集布局新版源代码集布局
The layout adds additional /kotlin SourceDirectoriessrc/{AndroidSourceSet.name}/kotlin, src/{KotlinSourceSet.name}/kotlin

{AndroidSourceSet.name} maps to {SourceDirectories included} as follows:

当前源代码集布局新版源代码集布局
mainsrc/androidMain/kotlin, src/main/kotlin, src/main/javasrc/androidMain/kotlin, src/main/kotlin, src/main/java
testsrc/androidTest/kotlin, src/test/kotlin, src/test/javasrc/androidUnitTest/kotlin, src/test/kotlin, src/test/java
androidTestsrc/androidAndroidTest/kotlin, src/androidTest/javasrc/androidInstrumentedTest/kotlin, src/androidTest/java, src/androidTest/kotlin

AndroidManifest.xml 文件的位置

当前源代码集布局新版源代码集布局
src/{AndroidSourceSet.name}/AndroidManifest.xmlsrc/{KotlinSourceSet.name}/AndroidManifest.xml

{AndroidSourceSet.name} maps to{AndroidManifest.xml location} as follows:

当前源代码集布局新版源代码集布局
mainsrc/main/AndroidManifest.xmlsrc/androidMain/AndroidManifest.xml
debugsrc/debug/AndroidManifest.xmlsrc/androidDebug/AndroidManifest.xml

The relation between Android and common tests

The new Android source set layout changes the relation between Android-instrumented tests (renamed to androidInstrumentedTest in the new layout) and common tests.

Previously, there was a default dependsOn relation between androidAndroidTest and commonTest. In practice, it meant the following:

  • The code in commonTest was available in androidAndroidTest.
  • expect declarations in commonTest had to have corresponding actual implementations in androidAndroidTest.
  • Tests declared in commonTest were also running as Android instrumented tests.

In the new Android source set layout, the dependsOn relation is not added by default. If you prefer the previous behavior, manually declare this relation in your build.gradle.kts file:

  1. kotlin {
  2. // ...
  3. sourceSets {
  4. val commonTest by getting
  5. val androidInstrumentedTest by getting {
  6. dependsOn(commonTest)
  7. }
  8. }
  9. }

Support for Android flavors

Previously, the Kotlin Gradle plugin eagerly created source sets that correspond to Android source sets with debug and release build types or custom flavors like demo and full. It made them accessible by constructions like val androidDebug by getting { ... }.

In the new Android source set layout, those source sets are created in the afterEvaluate phase. It makes such expressions invalid, leading to errors like org.gradle.api.UnknownDomainObjectException: KotlinSourceSet with name 'androidDebug' not found.

To work around that, use the new invokeWhenCreated() API in your build.gradle.kts file:

  1. kotlin {
  2. // ...
  3. sourceSets.invokeWhenCreated("androidFreeDebug") {
  4. // ...
  5. }
  6. }

配置与设置

The new layout will become the default in future releases. You can enable it now with the following Gradle option:

  1. kotlin.mpp.androidSourceSetLayoutVersion=2

The new layout requires Android Gradle plugin 7.0 or later and is supported in Android Studio 2022.3 and later.

Kotlin 1.8.0 - 图4

The usage of the previous Android-style directories is now discouraged. Kotlin 1.8.0 marks the start of the deprecation cycle, introducing a warning for the current layout. You can suppress the warning with the following Gradle property:

  1. kotlin.mpp.androidSourceSetLayoutVersion1.nowarn=true

Kotlin/JS

Kotlin 1.8.0 stabilizes the JS IR compiler backend and brings new features to JavaScript-related Gradle build scripts:

稳定版 JS IR 编译器后端

Starting with this release, the Kotlin/JS intermediate representation (IR-based) compiler backend is Stable. It took a while to unify infrastructure for all three backends, but they now work with the same IR for Kotlin code.

As a consequence of the stable JS IR compiler backend, the old one is deprecated from now on.

Incremental compilation is enabled by default along with the stable JS IR compiler.

If you still use the old compiler, switch your project to the new backend with the help of our migration guide.

报告 yarn.lock 已更新的新设置

If you use the yarn package manager, there are three new special Gradle settings that could notify you if the yarn.lock file has been updated. You can use these settings when you want to be notified if yarn.lock has been changed silently during the CI build process.

These three new Gradle properties are:

  • YarnLockMismatchReport, which specifies how changes to the yarn.lock file are reported. You can use one of the following values:
    • FAIL fails the corresponding Gradle task. This is the default.
    • WARNING writes the information about changes in the warning log.
    • NONE disables reporting.
  • reportNewYarnLock, which reports about the recently created yarn.lock file explicitly. By default, this option is disabled: it’s a common practice to generate a new yarn.lock file at the first start. You can use this option to ensure that the file has been committed to your repository.
  • yarnLockAutoReplace, which replaces yarn.lock automatically every time the Gradle task is run.

To use these options, update your build script file build.gradle.kts as follows:

  1. import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport
  2. import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension
  3. rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
  4. rootProject.the<YarnRootExtension>().yarnLockMismatchReport =
  5. YarnLockMismatchReport.WARNING // NONE | FAIL
  6. rootProject.the<YarnRootExtension>().reportNewYarnLock = false // true
  7. rootProject.the<YarnRootExtension>().yarnLockAutoReplace = false // true
  8. }

通过 Gradle 属性添加浏览器测试目标

Starting with Kotlin 1.8.0, you can set test targets for different browsers right in the Gradle properties file. Doing so shrinks the size of the build script file as you no longer need to write all targets in build.gradle.kts.

You can use this property to define a list of browsers for all modules, and then add specific browsers in the build scripts of particular modules.

For example, the following line in your Gradle property file will run the test in Firefox and Safari for all modules:

  1. kotlin.js.browser.karma.browsers=firefox,safari

See the full list of available values for the property on GitHub.

The Kotlin team is very grateful to Martynas Petuška for implementing this feature.

向项目添加 CSS 支持的新方式

This release provides a new approach to adding CSS support to your project. We assume that this will affect a lot of projects, so don’t forget to update your Gradle build script files as described below.

Before Kotlin 1.8.0, the cssSupport.enabled property was used to add CSS support:

  1. browser {
  2. commonWebpackConfig {
  3. cssSupport.enabled = true
  4. }
  5. }

Now you should use the enabled.set() method in the cssSupport {} block:

  1. browser {
  2. commonWebpackConfig {
  3. cssSupport {
  4. enabled.set(true)
  5. }
  6. }
  7. }

Gradle

Kotlin 1.8.0 fully supports Gradle versions 7.2 and 7.3. You can also use Gradle versions up to the latest Gradle release, but if you do, keep in mind that you might encounter deprecation warnings or some new Gradle features might not work.

This version brings lots of changes:

将 Kotlin 编译器选项暴露为 Gradle 惰性属性

To expose available Kotlin compiler options as Gradle lazy properties and to integrate them better into the Kotlin tasks, we made lots of changes:

  • Compile tasks have the new compilerOptions input, which is similar to the existing kotlinOptions but uses Property from the Gradle Properties API as the return type:

    1. tasks.named("compileKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile::class.java) {
    2. compilerOptions {
    3. useK2.set(true)
    4. }
    5. }
  • The Kotlin tools tasks KotlinJsDce and KotlinNativeLink have the new toolOptions input, which is similar to the existing kotlinOptions input.

  • New inputs have the @Nested Gradle annotation. Every property inside the inputs has a related Gradle annotation, such as @Input or @Internal.

  • The Kotlin Gradle plugin API artifact has two new interfaces:
    • org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask, which has the compilerOptions input and the compileOptions() method. All Kotlin compilation tasks implement this interface.
    • org.jetbrains.kotlin.gradle.tasks.KotlinToolTask, which has the toolOptions input and the toolOptions() method. All Kotlin tool tasks – KotlinJsDce, KotlinNativeLink, and KotlinNativeLinkArtifactTask – implement this interface.
  • Some compilerOptions use the new types instead of the String type:

    For example, you can use compilerOptions.jvmTarget.set(JvmTarget.JVM_11) instead of kotlinOptions.jvmTarget = "11".

    The kotlinOptions types didn’t change, and they are internally converted to compilerOptions types.

  • The Kotlin Gradle plugin API is binary-compatible with previous releases. There are, however, some source and ABI-breaking changes in the kotlin-gradle-plugin artifact. Most of these changes involve additional generic parameters to some internal types. One important change is that the KotlinNativeLink task no longer inherits the AbstractKotlinNativeCompile task.

  • KotlinJsCompilerOptions.outputFile and the related KotlinJsOptions.outputFile options are deprecated. Use the Kotlin2JsCompile.outputFileProperty task input instead.

The Kotlin Gradle plugin still adds the KotlinJvmOptions DSL to the Android extension:

  1. android {
  2. kotlinOptions {
  3. jvmTarget = "11"
  4. }
  5. }

This will be changed in the scope of this issue, when the compilerOptions DSL will be added to a module level.

Kotlin 1.8.0 - 图5

限制

The kotlinOptions task input and the kotlinOptions{...} task DSL are in support mode and will be deprecated in upcoming releases. Improvements will be made only to compilerOptions and toolOptions.

Kotlin 1.8.0 - 图6

Calling any setter or getter on kotlinOptions delegates to the related property in the compilerOptions. This introduces the following limitations:

  • compilerOptions and kotlinOptions cannot be changed in the task execution phase (see one exception in the paragraph below).
  • freeCompilerArgs returns an immutable List<String>, which means that, for example, kotlinOptions.freeCompilerArgs.remove("something") will fail.

Several plugins, including kotlin-dsl and the Android Gradle plugin (AGP) with Jetpack Compose enabled, try to modify the freeCompilerArgs attribute in the task execution phase. We’ve added a workaround for them in Kotlin 1.8.0. This workaround allows any build script or plugin to modify kotlinOptions.freeCompilerArgs in the execution phase but produces a warning in the build log. To disable this warning, use the new Gradle property kotlin.options.suppressFreeCompilerArgsModificationWarning=true. Gradle is going to add fixes for the kotlin-dsl plugin and AGP with Jetpack Compose enabled.

提高最低支持版本

Starting with Kotlin 1.8.0, the minimum supported Gradle version is 6.8.3 and the minimum supported Android Gradle plugin version is 4.1.3.

See the Kotlin Gradle plugin compatibility with available Gradle versions in our documentation

禁用 Kotlin 守护程序回退策略的能力

There is a new Gradle property kotlin.daemon.useFallbackStrategy, whose default value is true. When the value is false, builds fail on problems with the daemon’s startup or communication. There is also a new useDaemonFallbackStrategy property in Kotlin compile tasks, which takes priority over the Gradle property if you use both. If there is insufficient memory to run the compilation, you can see a message about it in the logs.

The Kotlin compiler’s fallback strategy is to run a compilation outside the Kotlin daemon if the daemon somehow fails. If the Gradle daemon is on, the compiler uses the “In process” strategy. If the Gradle daemon is off, the compiler uses the “Out of process” strategy. Learn more about these execution strategies in the documentation. Note that silent fallback to another strategy can consume a lot of system resources or lead to non-deterministic builds; see this YouTrack issue for more details.

在传递依赖项中使用最新的 kotlin-stdlib 版本

If you explicitly write Kotlin version 1.8.0 or higher in your dependencies, for example: implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.0"), then the Kotlin Gradle Plugin will use that Kotlin version for transitive kotlin-stdlib-jdk7 and kotlin-stdlib-jdk8 dependencies. This is done to avoid class duplication from different stdlib versions (learn more about merging kotlin-stdlib-jdk7 and kotlin-stdlib-jdk8 into kotlin-stdlib). You can disable this behavior with the kotlin.stdlib.jdk.variants.version.alignment Gradle property:

  1. kotlin.stdlib.jdk.variants.version.alignment=false

If you run into issues with version alignment, align all versions via the Kotlin BOM by declaring a platform dependency on kotlin-bom in your build script:

  1. implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))

Learn about other cases and our suggested solutions in the documentation.

强制检查相关 Kotlin 与 Java 编译任务的 JVM 目标

This section applies to your JVM project even if your source files are only in Kotlin and you don’t use Java.

Kotlin 1.8.0 - 图7

Starting from this release, the default value for the kotlin.jvm.target.validation.mode property is error for projects on Gradle 8.0+ (this version of Gradle has not been released yet), and the plugin will fail the build in the event of JVM target incompatibility.

The shift of the default value from warning to error is a preparation step for a smooth migration to Gradle 8.0. We encourage you to set this property to error and configure a toolchain or align JVM versions manually.

Learn more about what can go wrong if you don’t check the targets’ compatibility.

解决 Kotlin Gradle 插件的传递依赖项

In Kotlin 1.7.0, we introduced support for Gradle plugin variants. Because of these plugin variants, a build classpath can have different versions of the Kotlin Gradle plugins that depend on different versions of some dependency, usually kotlin-gradle-plugin-api. This can lead to a resolution problem, and we would like to propose the following workaround, using the kotlin-dsl plugin as an example.

The kotlin-dsl plugin in Gradle 7.6 depends on the org.jetbrains.kotlin.plugin.sam.with.receiver:1.7.10 plugin, which depends on kotlin-gradle-plugin-api:1.7.10. If you add the org.jetbrains.kotlin.gradle.jvm:1.8.0 plugin, this kotlin-gradle-plugin-api:1.7.10 transitive dependency may lead to a dependency resolution error because of a mismatch between the versions (1.8.0 and 1.7.10) and the variant attributes’ org.gradle.plugin.api-version values. As a workaround, add this constraint to align the versions. This workaround may be needed until we implement the Kotlin Gradle Plugin libraries alignment platform, which is in the plans:

  1. dependencies {
  2. constraints {
  3. implementation("org.jetbrains.kotlin:kotlin-sam-with-receiver:1.8.0")
  4. }
  5. }

This constraint forces the org.jetbrains.kotlin:kotlin-sam-with-receiver:1.8.0 version to be used in the build classpath for transitive dependencies. Learn more about one similar case in the Gradle issue tracker.

弃用与删除

In Kotlin 1.8.0, the deprecation cycle continues for the following properties and methods:

标准库

Kotlin 1.8.0:

更新了 JVM 编译项目标

In Kotlin 1.8.0, the standard libraries (kotlin-stdlib, kotlin-reflect, and kotlin-script-*) are compiled with JVM target 1.8. Previously, the standard libraries were compiled with JVM target 1.6.

Kotlin 1.8.0 no longer supports JVM targets 1.6 and 1.7. As a result, you no longer need to declare kotlin-stdlib-jdk7 and kotlin-stdlib-jdk8 separately in build scripts because the contents of these artifacts have been merged into kotlin-stdlib.

If you have explicitly declared kotlin-stdlib-jdk7 and kotlin-stdlib-jdk8 as dependencies in your build scripts, then you should replace them with kotlin-stdlib.

Kotlin 1.8.0 - 图8

Note that mixing different versions of stdlib artifacts could lead to class duplication or to missing classes. To avoid that, the Kotlin Gradle plugin can help you align stdlib versions.

cbrt()

The cbrt() function, which allows you to compute the real cube root of a double or float, is now Stable.

  1. import kotlin.math.*
  2. fun main() {
  3. val num = 27
  4. val negNum = -num
  5. println("The cube root of ${num.toDouble()} is: " +
  6. cbrt(num.toDouble()))
  7. println("The cube root of ${negNum.toDouble()} is: " +
  8. cbrt(negNum.toDouble()))
  9. }

Java 与 Kotlin 之间的 TimeUnit 转换

The toTimeUnit() and toDurationUnit() functions in kotlin.time are now Stable. Introduced as Experimental in Kotlin 1.6.0, these functions improve interoperability between Kotlin and Java. You can now easily convert between Java java.util.concurrent.TimeUnit and Kotlin kotlin.time.DurationUnit. These functions are supported on the JVM only.

  1. import kotlin.time.*
  2. // For use from Java
  3. fun wait(timeout: Long, unit: TimeUnit) {
  4. val duration: Duration = timeout.toDuration(unit.toDurationUnit())
  5. ...
  6. }

可比较可减去的 TimeMark

The new functionality of TimeMarks is Experimental, and to use it you need to opt in by using @OptIn(ExperimentalTime::class) or @ExperimentalTime.

Kotlin 1.8.0 - 图9

Before Kotlin 1.8.0, if you wanted to calculate the time difference between multiple TimeMarks and now, you could only call elapsedNow() on one TimeMark at a time. This made it difficult to compare the results because the two elapsedNow() function calls couldn’t be executed at exactly the same time.

To solve this, in Kotlin 1.8.0 you can subtract and compare TimeMarks from the same time source. Now you can create a new TimeMark instance to represent now and subtract other TimeMarks from it. This way, the results that you collect from these calculations are guaranteed to be relative to each other.

  1. import kotlin.time.*
  2. fun main() {
  3. //sampleStart
  4. val timeSource = TimeSource.Monotonic
  5. val mark1 = timeSource.markNow()
  6. Thread.sleep(500) // Sleep 0.5 seconds
  7. val mark2 = timeSource.markNow()
  8. // Before 1.8.0
  9. repeat(4) { n ->
  10. val elapsed1 = mark1.elapsedNow()
  11. val elapsed2 = mark2.elapsedNow()
  12. // Difference between elapsed1 and elapsed2 can vary depending
  13. // on how much time passes between the two elapsedNow() calls
  14. println("Measurement 1.${n + 1}: elapsed1=$elapsed1, " +
  15. "elapsed2=$elapsed2, diff=${elapsed1 - elapsed2}")
  16. }
  17. println()
  18. // Since 1.8.0
  19. repeat(4) { n ->
  20. val mark3 = timeSource.markNow()
  21. val elapsed1 = mark3 - mark1
  22. val elapsed2 = mark3 - mark2
  23. // Now the elapsed times are calculated relative to mark3,
  24. // which is a fixed value
  25. println("Measurement 2.${n + 1}: elapsed1=$elapsed1, " +
  26. "elapsed2=$elapsed2, diff=${elapsed1 - elapsed2}")
  27. }
  28. // It's also possible to compare time marks with each other
  29. // This is true, as mark2 was captured later than mark1
  30. println(mark2 > mark1)
  31. //sampleEnd
  32. }

This new functionality is particularly useful in animation calculations where you want to calculate the difference between, or compare, multiple TimeMarks representing different frames.

递归复制或删除目录

These new functions for java.nio.file.path are Experimental. To use them, you need to opt in with @OptIn(kotlin.io.path.ExperimentalPathApi::class) or @kotlin.io.path.ExperimentalPathApi. Alternatively, you can use the compiler option -opt-in=kotlin.io.path.ExperimentalPathApi.

Kotlin 1.8.0 - 图10

We have introduced two new extension functions for java.nio.file.Path, copyToRecursively() and deleteRecursively(), which allow you to recursively:

  • Copy a directory and its contents to another destination.
  • Delete a directory and its contents.

These functions can be very useful as part of a backup process.

错误处理

Using copyToRecursively(), you can define what should happen if an exception occurs while copying, by overloading the onError lambda function:

  1. sourceRoot.copyToRecursively(destinationRoot, followLinks = false,
  2. onError = { source, target, exception ->
  3. logger.logError(exception, "Failed to copy $source to $target")
  4. OnErrorResult.TERMINATE
  5. })

When you use deleteRecursively(), if an exception occurs while deleting a file or folder, then the file or folder is skipped. Once the deletion has completed, deleteRecursively() throws an IOException containing all the exceptions that occurred as suppressed exceptions.

文件覆盖

If copyToRecursively() finds that a file already exists in the destination directory, then an exception occurs. If you want to overwrite the file instead, use the overload that has overwrite as an argument and set it to true:

  1. fun setUpEnvironment(projectDirectory: Path, fixtureName: String) {
  2. fixturesRoot.resolve(COMMON_FIXTURE_NAME)
  3. .copyToRecursively(projectDirectory, followLinks = false)
  4. fixturesRoot.resolve(fixtureName)
  5. .copyToRecursively(projectDirectory, followLinks = false,
  6. overwrite = true) // patches the common fixture
  7. }

自定义复制动作

To define your own custom logic for copying, use the overload that has copyAction as an additional argument. By using copyAction you can provide a lambda function, for example, with your preferred actions:

  1. sourceRoot.copyToRecursively(destinationRoot, followLinks = false) { source, target ->
  2. if (source.name.startsWith(".")) {
  3. CopyActionResult.SKIP_SUBTREE
  4. } else {
  5. source.copyToIgnoringExistingDirectory(target, followLinks = false)
  6. CopyActionResult.CONTINUE
  7. }
  8. }

For more information on these extension functions, see our API reference.

Java Optional 扩展函数

The extension functions that were introduced in Kotlin 1.7.0 are now Stable. These functions simplify working with Optional classes in Java. They can be used to unwrap and convert Optional objects on the JVM, and to make working with Java APIs more concise. For more information, see What’s new in Kotlin 1.7.0.

改进了 kotlin-reflect 性能

Taking advantage of the fact that kotlin-reflect is now compiled with JVM target 1.8, we migrated our internal cache mechanism to Java’s ClassValue. Previously we only cached KClass, but we now also cache KType and KDeclarationContainer. These changes have led to significant performance improvements when invoking typeOf().

文档更新

The Kotlin documentation has received some notable changes:

修订与新增页面

  • Gradle overview – learn how to configure and build a Kotlin project with the Gradle build system, available compiler options, compilation, and caches in the Kotlin Gradle plugin.
  • Nullability in Java and Kotlin – see the differences between Java’s and Kotlin’s approaches to handling possibly nullable variables.
  • Lincheck guide – learn how to set up and use the Lincheck framework for testing concurrent algorithms on the JVM.

新增与更新教程

安装 Kotlin 1.8.0

IntelliJ IDEA 2021.3, 2022.1, and 2022.2 automatically suggest updating the Kotlin plugin to version 1.8.0. IntelliJ IDEA 2022.3 will have the 1.8.0 version of the Kotlin plugin bundled in an upcoming minor update.

To migrate existing projects to Kotlin 1.8.0 in IntelliJ IDEA 2022.3, change the Kotlin version to 1.8.0 and reimport your Gradle or Maven project.

Kotlin 1.8.0 - 图11

For Android Studio Electric Eel (221) and Flamingo (222), version 1.8.0 of the Kotlin plugin will be delivered with the upcoming Android Studios updates. The new command-line compiler is available for download on the GitHub release page.

Kotlin 1.8.0 的兼容性指南

Kotlin 1.8.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 these changes in the Compatibility guide for Kotlin 1.8.0.