CINXE.COM
What's new in Kotlin 2.0.20 | Kotlin Documentation
<!DOCTYPE html SYSTEM "about:legacy-compat"> <html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="built-on" content="2024-11-26T15:25:55.304563615"><meta name="build-number" content="3287"><script> (function (w, d, s, l, i) { w[l] = w[l] || []; w[l].push({'gtm.start': new Date().getTime(), event: 'gtm.js'}); var f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src = '//www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f); })(window, document, 'script', 'dataLayer', 'GTM-5P98'); </script> <script src="static/v3/analytics.js"></script> <title>What's new in Kotlin 2.0.20 | Kotlin Documentation</title><script type="application/json" id="virtual-toc-data">[{"id":"ide-support","level":0,"title":"IDE support","anchor":"#ide-support"},{"id":"language","level":0,"title":"Language","anchor":"#language"},{"id":"data-class-copy-function-to-have-the-same-visibility-as-constructor","level":1,"title":"Data class copy function to have the same visibility as constructor","anchor":"#data-class-copy-function-to-have-the-same-visibility-as-constructor"},{"id":"phased-replacement-of-context-receivers-with-context-parameters","level":1,"title":"Phased replacement of context receivers with context parameters","anchor":"#phased-replacement-of-context-receivers-with-context-parameters"},{"id":"kotlin-multiplatform","level":0,"title":"Kotlin Multiplatform","anchor":"#kotlin-multiplatform"},{"id":"static-accessors-for-source-sets-from-the-default-target-hierarchy","level":1,"title":"Static accessors for source sets from the default target hierarchy","anchor":"#static-accessors-for-source-sets-from-the-default-target-hierarchy"},{"id":"deprecated-compatibility-with-kotlin-multiplatform-gradle-plugin-and-gradle-java-plugins","level":1,"title":"Deprecated compatibility with Kotlin Multiplatform Gradle plugin and Gradle Java plugins","anchor":"#deprecated-compatibility-with-kotlin-multiplatform-gradle-plugin-and-gradle-java-plugins"},{"id":"kotlin-native","level":0,"title":"Kotlin/Native","anchor":"#kotlin-native"},{"id":"concurrent-marking-in-garbage-collector","level":1,"title":"Concurrent marking in garbage collector","anchor":"#concurrent-marking-in-garbage-collector"},{"id":"support-for-bitcode-embedding-removed","level":1,"title":"Support for bitcode embedding removed","anchor":"#support-for-bitcode-embedding-removed"},{"id":"changes-to-gc-performance-monitoring-with-signposts","level":1,"title":"Changes to GC performance monitoring with signposts","anchor":"#changes-to-gc-performance-monitoring-with-signposts"},{"id":"ability-to-call-kotlin-suspending-functions-from-swift-objective-c-on-non-main-threads","level":1,"title":"Ability to call Kotlin suspending functions from Swift/Objective-C on non-main threads","anchor":"#ability-to-call-kotlin-suspending-functions-from-swift-objective-c-on-non-main-threads"},{"id":"kotlin-wasm","level":0,"title":"Kotlin/Wasm","anchor":"#kotlin-wasm"},{"id":"error-in-default-export-usage","level":1,"title":"Error in default export usage","anchor":"#error-in-default-export-usage"},{"id":"new-location-of-experimentalwasmdsl-annotation","level":1,"title":"New location of ExperimentalWasmDsl annotation","anchor":"#new-location-of-experimentalwasmdsl-annotation"},{"id":"kotlin-js","level":0,"title":"Kotlin/JS","anchor":"#kotlin-js"},{"id":"support-for-using-kotlin-static-members-in-javascript","level":1,"title":"Support for using Kotlin static members in JavaScript","anchor":"#support-for-using-kotlin-static-members-in-javascript"},{"id":"ability-to-create-kotlin-collections-from-javascript","level":1,"title":"Ability to create Kotlin collections from JavaScript","anchor":"#ability-to-create-kotlin-collections-from-javascript"},{"id":"gradle","level":0,"title":"Gradle","anchor":"#gradle"},{"id":"deprecated-incremental-compilation-based-on-jvm-history-files","level":1,"title":"Deprecated incremental compilation based on JVM history files","anchor":"#deprecated-incremental-compilation-based-on-jvm-history-files"},{"id":"option-to-share-jvm-artifacts-between-projects-as-class-files","level":1,"title":"Option to share JVM artifacts between projects as class files","anchor":"#option-to-share-jvm-artifacts-between-projects-as-class-files"},{"id":"aligned-dependency-behavior-of-kotlin-gradle-plugin-with-java-test-fixtures-plugin","level":1,"title":"Aligned dependency behavior of Kotlin Gradle plugin with java-test-fixtures plugin","anchor":"#aligned-dependency-behavior-of-kotlin-gradle-plugin-with-java-test-fixtures-plugin"},{"id":"added-task-dependency-for-rare-cases-when-the-compile-task-lacks-one-on-an-artifact","level":1,"title":"Added task dependency for rare cases when the compile task lacks one on an artifact","anchor":"#added-task-dependency-for-rare-cases-when-the-compile-task-lacks-one-on-an-artifact"},{"id":"compose-compiler","level":0,"title":"Compose compiler","anchor":"#compose-compiler"},{"id":"fix-for-the-unnecessary-recompositions-issue-introduced-in-2-0-0","level":1,"title":"Fix for the unnecessary recompositions issue introduced in 2.0.0","anchor":"#fix-for-the-unnecessary-recompositions-issue-introduced-in-2-0-0"},{"id":"new-way-to-configure-compiler-options","level":1,"title":"New way to configure compiler options","anchor":"#new-way-to-configure-compiler-options"},{"id":"strong-skipping-mode-enabled-by-default","level":1,"title":"Strong skipping mode enabled by default","anchor":"#strong-skipping-mode-enabled-by-default"},{"id":"composition-trace-markers-enabled-by-default","level":1,"title":"Composition trace markers enabled by default","anchor":"#composition-trace-markers-enabled-by-default"},{"id":"non-skipping-group-optimizations","level":1,"title":"Non-skipping group optimizations","anchor":"#non-skipping-group-optimizations"},{"id":"support-for-default-parameters-in-abstract-composable-functions","level":1,"title":"Support for default parameters in abstract composable functions","anchor":"#support-for-default-parameters-in-abstract-composable-functions"},{"id":"standard-library","level":0,"title":"Standard library","anchor":"#standard-library"},{"id":"support-for-uuids-in-the-common-kotlin-standard-library","level":1,"title":"Support for UUIDs in the common Kotlin standard library","anchor":"#support-for-uuids-in-the-common-kotlin-standard-library"},{"id":"support-for-minlength-in-hexformat","level":1,"title":"Support for minLength in HexFormat","anchor":"#support-for-minlength-in-hexformat"},{"id":"changes-to-the-base64-s-decoder-behavior","level":1,"title":"Changes to the Base64\u0027s decoder behavior","anchor":"#changes-to-the-base64-s-decoder-behavior"},{"id":"documentation-updates","level":0,"title":"Documentation updates","anchor":"#documentation-updates"},{"id":"install-kotlin-2-0-20","level":0,"title":"Install Kotlin 2.0.20","anchor":"#install-kotlin-2-0-20"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="static/v3/app.css?v=6.11.0-footer" rel="stylesheet"><link rel="icon" type="image/svg" sizes="16x16" href="https://kotlinlang.org/assets/images/favicon.svg?v2"><link rel="icon" type="image/x-icon" sizes="32x32" href="https://kotlinlang.org/assets/images/favicon.ico?v2"><link rel="icon" type="image/png" sizes="96x96" href="https://kotlinlang.org/assets/images/apple-touch-icon.png?v2"><link rel="icon" type="image/png" sizes="300x300" href="https://kotlinlang.org/assets/images/apple-touch-icon-72x72.png?v2"><link rel="icon" type="image/png" sizes="500x500" href="https://kotlinlang.org/assets/images/apple-touch-icon-114x114.png?v2"><meta name="image" content="https://kotlinlang.org/assets/images/open-graph/docs.png"><!-- Open Graph --><meta property="og:title" content="What's new in Kotlin 2.0.20 | Kotlin"><meta property="og:description" content=""><meta property="og:image" content="https://kotlinlang.org/assets/images/open-graph/docs.png"><meta property="og:site_name" content="Kotlin Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="https://kotlinlang.org/docs/whatsnew2020.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content="@kotlin"><meta name="twitter:title" content="What's new in Kotlin 2.0.20 | Kotlin"><meta name="twitter:description" content=""><meta name="twitter:creator" content="@kotlin"><meta name="twitter:image:src" content="https://kotlinlang.org/assets/images/open-graph/docs.png"><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{ "@context": "http://schema.org", "@type": "WebPage", "@id": "https://kotlinlang.org/docs/whatsnew2020.html#webpage", "url": "https://kotlinlang.org/docs/whatsnew2020.html", "name": "What's new in Kotlin 2.0.20 | Kotlin", "description": "", "image": "https://kotlinlang.org/assets/images/open-graph/docs.png", "inLanguage":"en-US" }</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{ "@type": "WebSite", "@id": "https://kotlinlang.org/docs/#website", "url": "https://kotlinlang.org/docs/", "name": "Kotlin Help" }</script><!-- End Schema.org --><style>a[href="test-page.html"] { visibility: hidden; }</style></head><body data-id="whatsnew2020" data-main-title="What's new in Kotlin 2.0.20" data-article-props="{"seeAlsoStyle":"links"}" data-template="article" data-breadcrumbs="What's new in Kotlin" data-edit-url="https://github.com/JetBrains/kotlin-web-site/edit/master/docs/topics/whatsnew2020.md"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Kotlin Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="whatsnew2020" id="whatsnew2020.md">What's new in Kotlin 2.0.20</h1><p id="-8met8f_2"><span class="emphasis" id="-8met8f_16"><a href="releases.html#release-details" id="-8met8f_17">Released: August 22, 2024</a></span></p><p id="-8met8f_3">The Kotlin 2.0.20 release is out! This version includes performance improvements and bug fixes for Kotlin 2.0.0, where we announced the Kotlin K2 compiler as Stable. Here are some additional highlights from this release:</p><ul class="list _bullet" id="-8met8f_4"><li class="list__item" id="-8met8f_18"><p><a href="#data-class-copy-function-to-have-the-same-visibility-as-constructor" id="-8met8f_26">The data class copy function will have the same visibility as the constructor</a></p></li><li class="list__item" id="-8met8f_19"><p><a href="#static-accessors-for-source-sets-from-the-default-target-hierarchy" id="-8met8f_27">Static accessors for source sets from the default target hierarchy are now available in multiplatform projects</a></p></li><li class="list__item" id="-8met8f_20"><p><a href="#concurrent-marking-in-garbage-collector" id="-8met8f_28">Concurrent marking for Kotlin/Native has been made possible in the garbage collector</a></p></li><li class="list__item" id="-8met8f_21"><p><a href="#new-location-of-experimentalwasmdsl-annotation" id="-8met8f_29">The <code class="code" id="-8met8f_30">@ExperimentalWasmDsl</code> annotation in Kotlin/Wasm has a new location</a></p></li><li class="list__item" id="-8met8f_22"><p><a href="#gradle" id="-8met8f_31">Support has been added for Gradle versions 8.6–8.8</a></p></li><li class="list__item" id="-8met8f_23"><p><a href="#option-to-share-jvm-artifacts-between-projects-as-class-files" id="-8met8f_32">A new option allows sharing JVM artifacts between Gradle projects as class files</a></p></li><li class="list__item" id="-8met8f_24"><p><a href="#compose-compiler" id="-8met8f_33">The Compose compiler has been updated</a></p></li><li class="list__item" id="-8met8f_25"><p><a href="#support-for-uuids-in-the-common-kotlin-standard-library" id="-8met8f_34">Support for UUIDs has been added to the common Kotlin standard library</a></p></li></ul><section class="chapter"><h2 id="ide-support" data-toc="ide-support">IDE support</h2><p id="-8met8f_35">The Kotlin plugins that support 2.0.20 are bundled in the latest IntelliJ IDEA and Android Studio. You don't need to update the Kotlin plugin in your IDE. All you need to do is to <a href="configure-build-for-eap.html" id="-8met8f_37">change the Kotlin version</a> to 2.0.20 in your build scripts.</p><p id="-8met8f_36">See <a href="releases.html#update-to-a-new-kotlin-version" id="-8met8f_38">Update to a new release</a> for details.</p></section><section class="chapter"><h2 id="language" data-toc="language">Language</h2><p id="-8met8f_39">Kotlin 2.0.20 begins to introduce changes to improve consistency in data classes and replace the Experimental context receivers feature.</p><section class="chapter"><h3 id="data-class-copy-function-to-have-the-same-visibility-as-constructor" data-toc="data-class-copy-function-to-have-the-same-visibility-as-constructor">Data class copy function to have the same visibility as constructor</h3><p id="-8met8f_42">Currently, if you create a data class using a <code class="code" id="-8met8f_49">private</code> constructor, the automatically generated <code class="code" id="-8met8f_50">copy()</code> function doesn't have the same visibility. This can cause problems later in your code. In future Kotlin releases, we will introduce the behavior that the default visibility of the <code class="code" id="-8met8f_51">copy()</code> function is the same as the constructor. This change will be introduced gradually to help you migrate your code as smoothly as possible.</p><p id="-8met8f_43">Our migration plan starts with Kotlin 2.0.20, which issues warnings in your code where the visibility will change in the future. For example:</p><div class="code-block" data-lang="kotlin"> // Triggers a warning in 2.0.20 data class PositiveInteger private constructor(val number: Int) { companion object { fun create(number: Int): PositiveInteger? = if (number > 0) PositiveInteger(number) else null } } fun main() { val positiveNumber = PositiveInteger.create(42) ?: return // Triggers a warning in 2.0.20 val negativeNumber = positiveNumber.copy(number = -1) // Warning: Non-public primary constructor is exposed via the generated 'copy()' method of the 'data' class. // The generated 'copy()' will change its visibility in future releases. } </div><p id="-8met8f_45">For the latest information about our migration plan, see the corresponding issue in <a href="https://youtrack.jetbrains.com/issue/KT-11914" id="-8met8f_52" data-external="true" rel="noopener noreferrer">YouTrack</a>.</p><p id="-8met8f_46">To give you more control over this behavior, in Kotlin 2.0.20 we've introduced two annotations:</p><ul class="list _bullet" id="-8met8f_47"><li class="list__item" id="-8met8f_53"><p><code class="code" id="-8met8f_55">@ConsistentCopyVisibility</code> to opt in to the behavior now before we make it the default in a later release.</p></li><li class="list__item" id="-8met8f_54"><p><code class="code" id="-8met8f_56">@ExposedCopyVisibility</code> to opt out of the behavior and suppress warnings at the declaration site. Note that even with this annotation, the compiler still reports warnings when the <code class="code" id="-8met8f_57">copy()</code> function is called.</p></li></ul><p id="-8met8f_48">If you want to opt in to the new behavior already in 2.0.20 for a whole module rather than in individual classes, you can use the <code class="code" id="-8met8f_58">-Xconsistent-data-class-copy-visibility</code> compiler option. This option has the same effect as adding the <code class="code" id="-8met8f_59">@ConsistentCopyVisibility</code> annotation to all data classes in a module.</p></section><section class="chapter"><h3 id="phased-replacement-of-context-receivers-with-context-parameters" data-toc="phased-replacement-of-context-receivers-with-context-parameters">Phased replacement of context receivers with context parameters</h3><p id="-8met8f_60">In Kotlin 1.6.20, we introduced <a href="whatsnew1620.html#prototype-of-context-receivers-for-kotlin-jvm" id="-8met8f_69">context receivers</a> as an <a href="components-stability.html#stability-levels-explained" id="-8met8f_70">Experimental</a> feature. After listening to community feedback, we've decided not to continue with this approach and will take a different direction.</p><p id="-8met8f_61">In future Kotlin releases, context receivers will be replaced by context parameters. Context parameters are still in the design phase, and you can find the proposal in the <a href="https://github.com/Kotlin/KEEP/blob/context-parameters/proposals/context-parameters.md" id="-8met8f_71" data-external="true" rel="noopener noreferrer">KEEP</a>.</p><p id="-8met8f_62">Since the implementation of context parameters requires significant changes to the compiler, we've decided not to support context receivers and context parameters simultaneously. This decision greatly simplifies the implementation and minimizes the risk of unstable behavior.</p><p id="-8met8f_63">We understand that context receivers are already being used by a large number of developers. Therefore, we will begin gradually removing support for context receivers. Our migration plan starts with Kotlin 2.0.20, where warnings are issued in your code when context receivers are used with the <code class="code" id="-8met8f_72">-Xcontext-receivers</code> compiler option. For example:</p><div class="code-block" data-lang="kotlin"> class MyContext context(MyContext) // Warning: Experimental context receivers are deprecated and will be superseded by context parameters. // Please don't use context receivers. You can either pass parameters explicitly or use members with extensions. fun someFunction() { } </div><p id="-8met8f_65">This warning will become an error in future Kotlin releases.</p><p id="-8met8f_66">If you use context receivers in your code, we recommend that you migrate your code to use either of the following:</p><ul class="list _bullet" id="-8met8f_67"><li class="list__item" id="-8met8f_73"><p id="-8met8f_75">Explicit parameters.</p><div class="table-wrapper"><table class="wide" id="-8met8f_76"><thead><tr class="ijRowHead" id="-8met8f_77"><th id="-8met8f_79"><p>Before</p></th><th id="-8met8f_80"><p>After</p></th></tr></thead><tbody><tr id="-8met8f_78"><td id="-8met8f_81"><div class="code-block" data-lang="kotlin"> context(ContextReceiverType) fun someFunction() { contextReceiverMember() } </div></td><td id="-8met8f_82"><div class="code-block" data-lang="kotlin"> fun someFunction(explicitContext: ContextReceiverType) { explicitContext.contextReceiverMember() } </div></td></tr></tbody></table></div></li><li class="list__item" id="-8met8f_74"><p id="-8met8f_85">Extension member functions (if possible).</p><div class="table-wrapper"><table class="wide" id="-8met8f_86"><thead><tr class="ijRowHead" id="-8met8f_87"><th id="-8met8f_89"><p>Before</p></th><th id="-8met8f_90"><p>After</p></th></tr></thead><tbody><tr id="-8met8f_88"><td id="-8met8f_91"><div class="code-block" data-lang="kotlin"> context(ContextReceiverType) fun contextReceiverMember() = TODO() context(ContextReceiverType) fun someFunction() { contextReceiverMember() } </div></td><td id="-8met8f_92"><div class="code-block" data-lang="kotlin"> class ContextReceiverType { fun contextReceiverMember() = TODO() } fun ContextReceiverType.someFunction() { contextReceiverMember() } </div></td></tr></tbody></table></div></li></ul><p id="-8met8f_68">Alternatively, you can wait until the Kotlin release where context parameters are supported in the compiler. Note that context parameters will initially be introduced as an Experimental feature.</p></section></section><section class="chapter"><h2 id="kotlin-multiplatform" data-toc="kotlin-multiplatform">Kotlin Multiplatform</h2><p id="-8met8f_95">Kotlin 2.0.20 brings improvements to source set management in multiplatform projects as well as deprecates compatibility with some Gradle Java plugins due to recent changes in Gradle.</p><section class="chapter"><h3 id="static-accessors-for-source-sets-from-the-default-target-hierarchy" data-toc="static-accessors-for-source-sets-from-the-default-target-hierarchy">Static accessors for source sets from the default target hierarchy</h3><p id="-8met8f_98">Since Kotlin 1.9.20, the <a href="multiplatform-hierarchy.html#default-hierarchy-template" id="-8met8f_104">default hierarchy template</a> is automatically applied to all Kotlin Multiplatform projects. And for all of the source sets from the default hierarchy template, the Kotlin Gradle plugin provided type-safe accessors. That way, you could finally access source sets for all the specified targets without having to use <code class="code" id="-8met8f_105">by getting</code> or <code class="code" id="-8met8f_106">by creating</code> constructs.</p><p id="-8met8f_99">Kotlin 2.0.20 aims to improve your IDE experience even further. It now provides static accessors in the <code class="code" id="-8met8f_107">sourceSets {}</code> block for all the source sets from the default hierarchy template. We believe this change will make accessing source sets by name easier and more predictable.</p><p id="-8met8f_100">Each such source set now has a detailed KDoc comment with a sample and a diagnostic message with a warning in case you try to access the source set without declaring the corresponding target first:</p><div class="code-block" data-lang="kotlin"> kotlin { jvm() linuxX64() linuxArm64() mingwX64() sourceSets { commonMain.languageSettings { progressiveMode = true } jvmMain { } linuxX64Main { } linuxArm64Main { } // Warning: accessing source set without registering the target iosX64Main { } } } </div><figure id="-8met8f_102"><img alt="Accessing the source sets by name" src="images/accessing-sourse-sets.png" title="Accessing the source sets by name" width="700" height="312"></figure><p id="-8met8f_103">Learn more about the <a href="multiplatform-hierarchy.html" id="-8met8f_108">hierarchical project structure in Kotlin Multiplatform</a>.</p></section><section class="chapter"><h3 id="deprecated-compatibility-with-kotlin-multiplatform-gradle-plugin-and-gradle-java-plugins" data-toc="deprecated-compatibility-with-kotlin-multiplatform-gradle-plugin-and-gradle-java-plugins">Deprecated compatibility with Kotlin Multiplatform Gradle plugin and Gradle Java plugins</h3><p id="-8met8f_109">In Kotlin 2.0.20, we introduce a deprecation warning when you apply the Kotlin Multiplatform Gradle plugin and any of the following Gradle Java plugins to the same project: <a href="https://docs.gradle.org/current/userguide/java_plugin.html" id="-8met8f_128" data-external="true" rel="noopener noreferrer">Java</a>, <a href="https://docs.gradle.org/current/userguide/java_library_plugin.html" id="-8met8f_129" data-external="true" rel="noopener noreferrer">Java Library</a>, and <a href="https://docs.gradle.org/current/userguide/application_plugin.html" id="-8met8f_130" data-external="true" rel="noopener noreferrer">Application</a>. The warning also appears when another Gradle plugin in your multiplatform project applies a Gradle Java plugin. For example, the <a href="https://docs.spring.io/spring-boot/gradle-plugin/index.html" id="-8met8f_131" data-external="true" rel="noopener noreferrer">Spring Boot Gradle Plugin</a> automatically applies the Application plugin.</p><p id="-8met8f_110">We've added this deprecation warning due to fundamental compatibility issues between Kotlin Multiplatform's project model and Gradle's Java ecosystem plugins. Gradle's Java ecosystem plugins currently don't take into account that other plugins may:</p><ul class="list _bullet" id="-8met8f_111"><li class="list__item" id="-8met8f_132"><p>Also publish or compile for the JVM target in a different way than the Java ecosystem plugins.</p></li><li class="list__item" id="-8met8f_133"><p>Have two different JVM targets in the same project, such as JVM and Android.</p></li><li class="list__item" id="-8met8f_134"><p>Have a complex multiplatform project structure with potentially multiple non-JVM targets.</p></li></ul><p id="-8met8f_112">Unfortunately, Gradle doesn't currently provide any API to address these issues.</p><p id="-8met8f_113">We previously used some workarounds in Kotlin Multiplatform to help with the integration of Java ecosystem plugins. However, these workarounds never truly solved the compatibility issues, and since the release of Gradle 8.8, these workarounds are no longer possible. For more information, see our <a href="https://youtrack.jetbrains.com/issue/KT-66542/Gradle-JVM-target-with-withJava-produces-a-deprecation-warning" id="-8met8f_135" data-external="true" rel="noopener noreferrer">YouTrack issue</a>.</p><p id="-8met8f_114">While we don't yet know exactly how to resolve this compatibility problem, we are committed to continuing support for some form of Java source compilation in your Kotlin Multiplatform projects. At a minimum, we will support the compilation of Java sources and using Gradle's <a href="https://docs.gradle.org/current/javadoc/org/gradle/api/plugins/JavaBasePlugin.html" id="-8met8f_136" data-external="true" rel="noopener noreferrer"><code class="code" id="-8met8f_137">java-base</code></a> plugin within your multiplatform projects.</p><p id="-8met8f_115">In the meantime, if you see this deprecation warning in your multiplatform project, we recommend that you:</p><ol class="list _decimal" id="-8met8f_116" type="1"><li class="list__item" id="-8met8f_138"><p>Determine whether you actually need the Gradle Java plugin in your project. If not, consider removing it.</p></li><li class="list__item" id="-8met8f_139"><p>Check if the Gradle Java plugin is only used for a single task. If so, you might be able to remove the plugin without much effort. For example, if the task uses a Gradle Java plugin to create a Javadoc JAR file, you can define the Javadoc task manually instead.</p></li></ol><p id="-8met8f_117">Otherwise, if you want to use both the Kotlin Multiplatform Gradle plugin and these Gradle plugins for Java in your multiplatform project, we recommend that you:</p><ol class="list _decimal" id="-8met8f_118" type="1"><li class="list__item" id="-8met8f_140"><p>Create a separate subproject in your multiplatform project.</p></li><li class="list__item" id="-8met8f_141"><p>In the separate subproject, apply the Gradle plugin for Java.</p></li><li class="list__item" id="-8met8f_142"><p>In the separate subproject, add a dependency on your parent multiplatform project.</p></li></ol><aside class="prompt" data-type="warning" data-title="" id="-8met8f_119"><p id="-8met8f_143">The separate subproject must <span class="control" id="-8met8f_144">not</span> be a multiplatform project, and you must only use it to set up a dependency on your multiplatform project.</p></aside><p id="-8met8f_120">For example, you have a multiplatform project called <code class="code" id="-8met8f_145">my-main-project</code> and you want to use the <a href="https://docs.gradle.org/current/userguide/application_plugin.html" id="-8met8f_146" data-external="true" rel="noopener noreferrer">Application</a> Gradle plugin to run a JVM application.</p><p id="-8met8f_121">Once you've created a subproject, let's call it <code class="code" id="-8met8f_147">subproject-A</code>, your parent project structure should look like this:</p><div class="code-block" data-lang="plaintext"> . ├── build.gradle.kts ├── settings.gradle ├── subproject-A └── build.gradle.kts └── src └── Main.java </div><p id="-8met8f_123">In your subproject's <code class="code" id="-8met8f_148">build.gradle.kts</code> file, apply the Application plugin in the <code class="code" id="-8met8f_149">plugins {}</code> block:</p><div class="tabs" id="-8met8f_124" data-group="build-script" data-anchors="[-8met8f_150,-8met8f_151]"><div class="tabs__content" data-gtm="tab" id="-8met8f_150" data-sync-tabs="kotlin" data-title="Kotlin"><div class="code-block" data-lang="kotlin" data-title="Kotlin"> plugins { id("application") } </div></div><div class="tabs__content" data-gtm="tab" id="-8met8f_151" data-sync-tabs="groovy" data-title="Groovy"><div class="code-block" data-lang="groovy" data-title="Groovy"> plugins { id('application') } </div></div></div><p id="-8met8f_125">In your subproject's <code class="code" id="-8met8f_154">build.gradle.kts</code> file, add a dependency on your parent multiplatform project:</p><div class="tabs" id="-8met8f_126" data-group="build-script" data-anchors="[-8met8f_155,-8met8f_156]"><div class="tabs__content" data-gtm="tab" id="-8met8f_155" data-sync-tabs="kotlin" data-title="Kotlin"><div class="code-block" data-lang="kotlin" data-title="Kotlin"> dependencies { implementation(project(":my-main-project")) // The name of your parent multiplatform project } </div></div><div class="tabs__content" data-gtm="tab" id="-8met8f_156" data-sync-tabs="groovy" data-title="Groovy"><div class="code-block" data-lang="groovy" data-title="Groovy"> dependencies { implementation project(':my-main-project') // The name of your parent multiplatform project } </div></div></div><p id="-8met8f_127">Your parent project is now set up to work with both plugins.</p></section></section><section class="chapter"><h2 id="kotlin-native" data-toc="kotlin-native">Kotlin/Native</h2><p id="-8met8f_159">Kotlin/Native receives improvements in the garbage collector and for calling Kotlin suspending functions from Swift/Objective-C.</p><section class="chapter"><h3 id="concurrent-marking-in-garbage-collector" data-toc="concurrent-marking-in-garbage-collector">Concurrent marking in garbage collector</h3><p id="-8met8f_164">In Kotlin 2.0.20, the JetBrains team takes another step toward improving Kotlin/Native runtime performance. We've added experimental support for concurrent marking in the garbage collector (GC).</p><p id="-8met8f_165">By default, application threads must be paused when GC is marking objects in the heap. This greatly affects the duration of the GC pause time, which is important for the performance of latency-critical applications, such as UI applications built with Compose Multiplatform.</p><p id="-8met8f_166">Now, the marking phase of the garbage collection can be run simultaneously with application threads. This should significantly shorten the GC pause time and help improve app responsiveness.</p><section class="chapter"><h4 id="how-to-enable" data-toc="how-to-enable">How to enable</h4><p id="-8met8f_168">The feature is currently <a href="components-stability.html#stability-levels-explained" id="-8met8f_171">Experimental</a>. To enable it, set the following option in your <code class="code" id="-8met8f_172">gradle.properties</code> file:</p><div class="code-block" data-lang="none"> kotlin.native.binary.gc=cms </div><p id="-8met8f_170">Please report any problems to our issue tracker <a href="https://kotl.in/issue" id="-8met8f_173" data-external="true" rel="noopener noreferrer">YouTrack</a>.</p></section></section><section class="chapter"><h3 id="support-for-bitcode-embedding-removed" data-toc="support-for-bitcode-embedding-removed">Support for bitcode embedding removed</h3><p id="-8met8f_174">Starting with Kotlin 2.0.20, the Kotlin/Native compiler no longer supports bitcode embedding. Bitcode embedding was deprecated in Xcode 14 and removed in Xcode 15 for all Apple targets.</p><p id="-8met8f_175">Now, the <code class="code" id="-8met8f_177">embedBitcode</code> parameter for the framework configuration, as well as the <code class="code" id="-8met8f_178">-Xembed-bitcode</code> and <code class="code" id="-8met8f_179">-Xembed-bitcode-marker</code> command line arguments are deprecated.</p><p id="-8met8f_176">If you still use earlier versions of Xcode but want to upgrade to Kotlin 2.0.20, disable bitcode embedding in your Xcode projects.</p></section><section class="chapter"><h3 id="changes-to-gc-performance-monitoring-with-signposts" data-toc="changes-to-gc-performance-monitoring-with-signposts">Changes to GC performance monitoring with signposts</h3><p id="-8met8f_180">Kotlin 2.0.0 made it possible to monitor the performance of the Kotlin/Native garbage collector (GC) through Xcode Instruments. Instruments include the signposts tool, which can show GC pauses as events. This comes in handy when checking GC-related freezes in your iOS apps.</p><p id="-8met8f_181">The feature was enabled by default, but unfortunately, it sometimes led to crashes when the application was run simultaneously with Xcode Instruments. Starting with Kotlin 2.0.20, it requires an explicit opt-in with the following compiler option:</p><div class="code-block" data-lang="none"> -Xbinary=enableSafepointSignposts=true </div><p id="-8met8f_183">Learn more about GC performance analysis in the <a href="native-memory-manager.html#monitor-gc-performance" id="-8met8f_184">documentation</a>.</p></section><section class="chapter"><h3 id="ability-to-call-kotlin-suspending-functions-from-swift-objective-c-on-non-main-threads" data-toc="ability-to-call-kotlin-suspending-functions-from-swift-objective-c-on-non-main-threads">Ability to call Kotlin suspending functions from Swift/Objective-C on non-main threads</h3><p id="-8met8f_185">Previously, Kotlin/Native had a default restriction, limiting the ability to call Kotlin suspending functions from Swift and Objective-C to only the main thread. Kotlin 2.0.20 lifts that limitation, allowing you to run Kotlin <code class="code" id="-8met8f_187">suspend</code> functions from Swift/Objective-C on any thread.</p><p id="-8met8f_186">If you've previously switched the default behavior for non-main threads with the <code class="code" id="-8met8f_188">kotlin.native.binary.objcExportSuspendFunctionLaunchThreadRestriction=none</code> binary option, you can now remove it from your <code class="code" id="-8met8f_189">gradle.properties</code> file.</p></section></section><section class="chapter"><h2 id="kotlin-wasm" data-toc="kotlin-wasm">Kotlin/Wasm</h2><p id="-8met8f_190">In Kotlin 2.0.20, Kotlin/Wasm continues the migration towards named exports and relocates the <code class="code" id="-8met8f_193">@ExperimentalWasmDsl</code> annotation.</p><section class="chapter"><h3 id="error-in-default-export-usage" data-toc="error-in-default-export-usage">Error in default export usage</h3><p id="-8met8f_194">As part of the migration towards named exports, a warning message was previously printed to the console when using a default import for Kotlin/Wasm exports in JavaScript.</p><p id="-8met8f_195">To fully support named exports, this warning has now been upgraded to an error. If you use a default import, you encounter the following error message:</p><div class="code-block" data-lang="plaintext"> Do not use default import. Use the corresponding named import instead. </div><p id="-8met8f_197">This change is part of a deprecation cycle to migrate towards named exports. Here's what you can expect during each phase:</p><ul class="list _bullet" id="-8met8f_198"><li class="list__item" id="-8met8f_199"><p><span class="control" id="-8met8f_202">In version 2.0.0</span>: A warning message is printed to the console, explaining that exporting entities via default exports is deprecated.</p></li><li class="list__item" id="-8met8f_200"><p><span class="control" id="-8met8f_203">In version 2.0.20</span>: An error occurs, requesting the use of the corresponding named import.</p></li><li class="list__item" id="-8met8f_201"><p><span class="control" id="-8met8f_204">In version 2.1.0</span>: The use of default imports is completely removed.</p></li></ul></section><section class="chapter"><h3 id="new-location-of-experimentalwasmdsl-annotation" data-toc="new-location-of-experimentalwasmdsl-annotation">New location of ExperimentalWasmDsl annotation</h3><p id="-8met8f_205">Previously, the <code class="code" id="-8met8f_214">@ExperimentalWasmDsl</code> annotation for WebAssembly (Wasm) features was placed in this location within the Kotlin Gradle plugin:</p><div class="code-block" data-lang="kotlin"> org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl </div><p id="-8met8f_207">In 2.0.20, the <code class="code" id="-8met8f_215">@ExperimentalWasmDsl</code> annotation has been relocated to:</p><div class="code-block" data-lang="kotlin"> org.jetbrains.kotlin.gradle.ExperimentalWasmDsl </div><p id="-8met8f_209">The previous location is now deprecated and might lead to build failures with unresolved references.</p><p id="-8met8f_210">To reflect the new location of the <code class="code" id="-8met8f_216">@ExperimentalWasmDsl</code> annotation, update the import statement in your Gradle build scripts. Use an explicit import for the new <code class="code" id="-8met8f_217">@ExperimentalWasmDsl</code> location:</p><div class="code-block" data-lang="kotlin"> import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl </div><p id="-8met8f_212">Alternatively, remove this star import statement from the old package:</p><div class="code-block" data-lang="kotlin"> import org.jetbrains.kotlin.gradle.targets.js.dsl.* </div></section></section><section class="chapter"><h2 id="kotlin-js" data-toc="kotlin-js">Kotlin/JS</h2><p id="-8met8f_218">Kotlin/JS introduces some Experimental features to support static members in JavaScript and to create Kotlin collections from JavaScript.</p><section class="chapter"><h3 id="support-for-using-kotlin-static-members-in-javascript" data-toc="support-for-using-kotlin-static-members-in-javascript">Support for using Kotlin static members in JavaScript</h3><aside class="prompt" data-type="warning" data-title="" id="-8met8f_221"><p id="-8met8f_228">This feature is <a href="components-stability.html#stability-levels-explained" id="-8met8f_229">Experimental</a>. It may be dropped or changed at any time. Use it only for evaluation purposes. We would appreciate your feedback on it in <a href="https://youtrack.jetbrains.com/issue/KT-18891/JS-provide-a-way-to-declare-static-members-JsStatic" id="-8met8f_230" data-external="true" rel="noopener noreferrer">YouTrack</a>.</p></aside><p id="-8met8f_222">Starting with Kotlin 2.0.20, you can use the <code class="code" id="-8met8f_231">@JsStatic</code> annotation. It works similarly to <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-static/" id="-8met8f_232" data-external="true" rel="noopener noreferrer">@JvmStatic</a> and instructs the compiler to generate additional static methods for the target declaration. This helps you use static members from your Kotlin code directly in JavaScript.</p><p id="-8met8f_223">You can use the <code class="code" id="-8met8f_233">@JsStatic</code> annotation for functions defined in named objects, as well as in companion objects declared inside classes and interfaces. The compiler generates both a static method of the object and an instance method in the object itself. For example:</p><div class="code-block" data-lang="kotlin"> class C { companion object { @JsStatic fun callStatic() {} fun callNonStatic() {} } } </div><p id="-8met8f_225">Now, <code class="code" id="-8met8f_234">callStatic()</code> is static in JavaScript while <code class="code" id="-8met8f_235">callNonStatic()</code> is not:</p><div class="code-block" data-lang="javascript"> C.callStatic(); // Works, accessing the static function C.callNonStatic(); // Error, not a static function in the generated JavaScript C.Companion.callStatic(); // Instance method remains C.Companion.callNonStatic(); // The only way it works </div><p id="-8met8f_227">It's also possible to apply the <code class="code" id="-8met8f_236">@JsStatic</code> annotation to a property of an object or a companion object, making its getter and setter methods static members in that object or the class containing the companion object.</p></section><section class="chapter"><h3 id="ability-to-create-kotlin-collections-from-javascript" data-toc="ability-to-create-kotlin-collections-from-javascript">Ability to create Kotlin collections from JavaScript</h3><aside class="prompt" data-type="warning" data-title="" id="-8met8f_237"><p id="-8met8f_245">This feature is <a href="components-stability.html#stability-levels-explained" id="-8met8f_246">Experimental</a>. It may be dropped or changed at any time. Use it only for evaluation purposes. We would appreciate your feedback on it in <a href="https://youtrack.jetbrains.com/issue/KT-69133/Kotlin-JS-Add-support-for-collection-instantiation-in-JavaScript" id="-8met8f_247" data-external="true" rel="noopener noreferrer">YouTrack</a>.</p></aside><p id="-8met8f_238">Kotlin 2.0.0 introduced the ability to export Kotlin collections to JavaScript (and TypeScript). Now, the JetBrains team is taking another step to improve collection interoperability. Starting with Kotlin 2.0.20, it's possible to create Kotlin collections directly from the JavaScript/TypeScript side.</p><p id="-8met8f_239">You can create Kotlin collections from JavaScript and pass them as arguments to the exported constructors or functions. As soon as you mention a collection inside an exported declaration, Kotlin generates a factory for the collection that is available in JavaScript/TypeScript.</p><p id="-8met8f_240">Take a look at the following exported function:</p><div class="code-block" data-lang="kotlin"> // Kotlin @JsExport fun consumeMutableMap(map: MutableMap<String, Int>) </div><p id="-8met8f_242">Since the <code class="code" id="-8met8f_248">MutableMap</code> collection is mentioned, Kotlin generates an object with a factory method available from JavaScript/TypeScript. This factory method then creates a <code class="code" id="-8met8f_249">MutableMap</code> from a JavaScript <code class="code" id="-8met8f_250">Map</code>:</p><div class="code-block" data-lang="javascript"> // JavaScript import { consumeMutableMap } from "an-awesome-kotlin-module" import { KtMutableMap } from "an-awesome-kotlin-module/kotlin-kotlin-stdlib" consumeMutableMap( KtMutableMap.fromJsMap(new Map([["First", 1], ["Second", 2]])) ) </div><p id="-8met8f_244">This feature is available for the <code class="code" id="-8met8f_251">Set</code>, <code class="code" id="-8met8f_252">Map</code>, and <code class="code" id="-8met8f_253">List</code> Kotlin collection types and their mutable counterparts.</p></section></section><section class="chapter"><h2 id="gradle" data-toc="gradle">Gradle</h2><p id="-8met8f_254">Kotlin 2.0.20 is fully compatible with Gradle 6.8.3 through 8.6. Gradle 8.7 and 8.8 are also supported, with only one exception: If you use the Kotlin Multiplatform Gradle plugin, you may see deprecation warnings in your multiplatform projects calling the <a href="multiplatform-dsl-reference.html#jvm-targets" id="-8met8f_262"><code class="code" id="-8met8f_263">withJava()</code> function in the JVM target</a>. We plan to fix this issue as soon as possible.</p><p id="-8met8f_255">For more information, see the issue in <a href="https://youtrack.jetbrains.com/issue/KT-66542/Gradle-JVM-target-with-withJava-produces-a-deprecation-warning" id="-8met8f_264" data-external="true" rel="noopener noreferrer">YouTrack</a>.</p><p id="-8met8f_256">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.</p><p id="-8met8f_257">This version brings changes such as beginning the deprecation process for the old incremental compilation approach based on JVM history files, as well as a new way of sharing JVM artifacts between projects.</p><section class="chapter"><h3 id="deprecated-incremental-compilation-based-on-jvm-history-files" data-toc="deprecated-incremental-compilation-based-on-jvm-history-files">Deprecated incremental compilation based on JVM history files</h3><p id="-8met8f_265">In Kotlin 2.0.20, the incremental compilation approach based on JVM history files is deprecated in favor of the new incremental compilation approach that has been enabled by default since Kotlin 1.8.20.</p><p id="-8met8f_266">The incremental compilation approach based on JVM history files suffered from limitations, such as not working with <a href="https://docs.gradle.org/current/userguide/build_cache.html" id="-8met8f_268" data-external="true" rel="noopener noreferrer">Gradle's build cache</a> and not supporting compilation avoidance. In contrast, the new incremental compilation approach overcomes these limitations and has performed well since its introduction.</p><p id="-8met8f_267">Given that the new incremental compilation approach has been used by default for the last two major Kotlin releases, the <code class="code" id="-8met8f_269">kotlin.incremental.useClasspathSnapshot</code> Gradle property is deprecated in Kotlin 2.0.20. Therefore, if you use it to opt out, you will see a deprecation warning.</p></section><section class="chapter"><h3 id="option-to-share-jvm-artifacts-between-projects-as-class-files" data-toc="option-to-share-jvm-artifacts-between-projects-as-class-files">Option to share JVM artifacts between projects as class files</h3><aside class="prompt" data-type="warning" data-title="" id="-8met8f_270"><p id="-8met8f_278">This feature is <a href="components-stability.html#stability-levels-explained" id="-8met8f_279">Experimental</a>. It may be dropped or changed at any time. Use it only for evaluation purposes. We would appreciate your feedback on it in <a href="https://youtrack.jetbrains.com/issue/KT-61861/Gradle-Kotlin-compilations-depend-on-packed-artifacts" id="-8met8f_280" data-external="true" rel="noopener noreferrer">YouTrack</a>. Opt-in is required (see details below).</p></aside><p id="-8met8f_271">In Kotlin 2.0.20, we introduce a new approach that changes the way the outputs of Kotlin/JVM compilations, such as JAR files, are shared between projects. With this approach, Gradle's <code class="code" id="-8met8f_281">apiElements</code> configuration now has a secondary variant that provides access to the directory containing compiled <code class="code" id="-8met8f_282">.class</code> files. When configured, your project uses this directory instead of requesting the compressed JAR artifact during compilation. This reduces the number of times JAR files are compressed and decompressed, especially for incremental builds.</p><p id="-8met8f_272">Our testing shows that this new approach can provide build performance improvements for Linux and macOS hosts. However, on Windows hosts, we have seen a degradation in performance due to how Windows handles I/O operations when working with files.</p><p id="-8met8f_273">To try this new approach, add the following property to your <code class="code" id="-8met8f_283">gradle.properties</code> file:</p><div class="code-block" data-lang="none"> kotlin.jvm.addClassesVariant=true </div><p id="-8met8f_275">By default, this property is set to <code class="code" id="-8met8f_284">false</code> and the <code class="code" id="-8met8f_285">apiElements</code> variant in Gradle requests the compressed JAR artifact.</p><aside class="prompt" data-type="note" data-title="" id="-8met8f_276"><p id="-8met8f_286">Gradle has a related property that you can use in your Java-only projects to only expose the compressed JAR artifact during compilation <span class="control" id="-8met8f_289">instead</span> of the directories containing compiled <code class="code" id="-8met8f_290">.class</code> files:</p><div class="code-block" data-lang="none"> org.gradle.java.compile-classpath-packaging=true </div><p id="-8met8f_288">For more information on this property and its purpose, see Gradle's documentation on the <a href="https://docs.gradle.org/current/userguide/java_library_plugin.html#sub:java_library_known_issues_windows_performance" id="-8met8f_291" data-external="true" rel="noopener noreferrer">Significant build performance drop on Windows for huge multi-projects</a>.</p></aside><p id="-8met8f_277">We would appreciate your feedback on this new approach. Have you noticed any performance improvements while using it? Let us know by adding a comment in <a href="https://youtrack.jetbrains.com/issue/KT-61861/Gradle-Kotlin-compilations-depend-on-packed-artifacts" id="-8met8f_292" data-external="true" rel="noopener noreferrer">YouTrack</a>.</p></section><section class="chapter"><h3 id="aligned-dependency-behavior-of-kotlin-gradle-plugin-with-java-test-fixtures-plugin" data-toc="aligned-dependency-behavior-of-kotlin-gradle-plugin-with-java-test-fixtures-plugin">Aligned dependency behavior of Kotlin Gradle plugin with java-test-fixtures plugin</h3><p id="-8met8f_293">Prior to Kotlin 2.0.20, if you used the <a href="https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures" id="-8met8f_300" data-external="true" rel="noopener noreferrer"><code class="code" id="-8met8f_301">java-test-fixtures</code> plugin</a> in your project, there was a difference between Gradle and the Kotlin Gradle plugin in how dependencies were propagated.</p><p id="-8met8f_294">The Kotlin Gradle plugin propagated dependencies:</p><ul class="list _bullet" id="-8met8f_295"><li class="list__item" id="-8met8f_302"><p>From the <code class="code" id="-8met8f_304">java-test-fixtures</code> plugin's <code class="code" id="-8met8f_305">implementation</code> and <code class="code" id="-8met8f_306">api</code> dependency types to the <code class="code" id="-8met8f_307">test</code> source set compilation classpath.</p></li><li class="list__item" id="-8met8f_303"><p>From the main source set's <code class="code" id="-8met8f_308">implementation</code> and <code class="code" id="-8met8f_309">api</code> dependency types to the <code class="code" id="-8met8f_310">java-test-fixtures</code> plugin's source set compilation classpath.</p></li></ul><p id="-8met8f_296">However, Gradle only propagated dependencies in the <code class="code" id="-8met8f_311">api</code> dependency types.</p><p id="-8met8f_297">This difference in behavior led to some projects finding resource files multiple times in the classpath.</p><p id="-8met8f_298">As of Kotlin 2.0.20, the Kotlin Gradle plugin's behavior is aligned with Gradle's <code class="code" id="-8met8f_312">java-test-fixtures</code> plugin so this problem no longer occurs for this or other Gradle plugins.</p><p id="-8met8f_299">As a result of this change, some dependencies in the <code class="code" id="-8met8f_313">test</code> and <code class="code" id="-8met8f_314">testFixtures</code> source sets may no longer be accessible. If this happens, either change the dependency declaration type from <code class="code" id="-8met8f_315">implementation</code> to <code class="code" id="-8met8f_316">api</code> or add a new dependency declaration on the affected source set.</p></section><section class="chapter"><h3 id="added-task-dependency-for-rare-cases-when-the-compile-task-lacks-one-on-an-artifact" data-toc="added-task-dependency-for-rare-cases-when-the-compile-task-lacks-one-on-an-artifact">Added task dependency for rare cases when the compile task lacks one on an artifact</h3><p id="-8met8f_317">Prior to 2.0.20, we found that there were scenarios where a compile task was missing a task dependency for one of its artifact inputs. This meant that the result of the dependent compile task was unstable, as sometimes the artifact had been generated in time, but sometimes, it hadn't.</p><p id="-8met8f_318">To fix this issue, the Kotlin Gradle plugin now automatically adds the required task dependency in these scenarios.</p><p id="-8met8f_319">In very rare cases, we've found that this new behavior can cause a circular dependency error. For example, if you have multiple compilations where one compilation can see all internal declarations of the other, and the generated artifact relies on the output of both compilation tasks, you could see an error like:</p><div class="code-block" data-lang="none"> FAILURE: Build failed with an exception. What went wrong: Circular dependency between the following tasks: :lib:compileKotlinJvm --- :lib:jvmJar \--- :lib:compileKotlinJvm (*) (*) - details omitted (listed previously) </div><p id="-8met8f_321">To fix this circular dependency error, we've added a Gradle property: <code class="code" id="-8met8f_325">archivesTaskOutputAsFriendModule</code>.</p><p id="-8met8f_322">By default, this property is set to <code class="code" id="-8met8f_326">true</code> to track the task dependency. To disable the use of the artifact in the compilation task, so that no task dependency is required, add the following in your <code class="code" id="-8met8f_327">gradle.properties</code> file:</p><div class="code-block" data-lang="kotlin"> kotlin.build.archivesTaskOutputAsFriendModule=false </div><p id="-8met8f_324">For more information, see the issue in <a href="https://youtrack.jetbrains.com/issue/KT-69330" id="-8met8f_328" data-external="true" rel="noopener noreferrer">YouTrack</a>.</p></section></section><section class="chapter"><h2 id="compose-compiler" data-toc="compose-compiler">Compose compiler</h2><p id="-8met8f_329">In Kotlin 2.0.20, the Compose compiler gets a few improvements.</p><section class="chapter"><h3 id="fix-for-the-unnecessary-recompositions-issue-introduced-in-2-0-0" data-toc="fix-for-the-unnecessary-recompositions-issue-introduced-in-2-0-0">Fix for the unnecessary recompositions issue introduced in 2.0.0</h3><p id="-8met8f_336">Compose compiler 2.0.0 has an issue where it sometimes incorrectly infers the stability of types in multiplatform projects with non-JVM targets. This can lead to unnecessary (or even endless) recompositions. We strongly recommended updating your Compose apps made for Kotlin 2.0.0 to version 2.0.10 or newer.</p><p id="-8met8f_337">If your app is built with Compose compiler 2.0.10 or newer but uses dependencies built with version 2.0.0, these older dependencies may still cause recomposition issues. To prevent this, update your dependencies to versions built with the same Compose compiler as your app.</p></section><section class="chapter"><h3 id="new-way-to-configure-compiler-options" data-toc="new-way-to-configure-compiler-options">New way to configure compiler options</h3><p id="-8met8f_338">We've introduced a new option configuration mechanism to avoid the churn of top-level parameters. It's harder for the Compose compiler team to test things out by creating or removing top-level entries for the <code class="code" id="-8met8f_345">composeCompiler {}</code> block. So, options such as strong skipping mode and non-skipping group optimizations are now enabled through the <code class="code" id="-8met8f_346">featureFlags</code> property. This property will be used to test new Compose compiler options that will eventually become default.</p><p id="-8met8f_339">This change has also been applied to the Compose compiler Gradle plugin. To configure feature flags going forward, use the following syntax (this code will flip all of the default values):</p><div class="code-block" data-lang="kotlin"> composeCompiler { featureFlags = setOf( ComposeFeatureFlag.IntrinsicRemember.disabled(), ComposeFeatureFlag.OptimizeNonSkippingGroups, ComposeFeatureFlag.StrongSkipping.disabled() ) } </div><p id="-8met8f_341">Or, if you are configuring the Compose compiler directly, use the following syntax:</p><div class="code-block" data-lang="plaintext"> -P plugin:androidx.compose.compiler.plugins.kotlin:featureFlag=IntrinsicRemember </div><p id="-8met8f_343">The <code class="code" id="-8met8f_347">enableIntrinsicRemember</code>, <code class="code" id="-8met8f_348">enableNonSkippingGroupOptimization</code>, and <code class="code" id="-8met8f_349">enableStrongSkippingMode</code> properties have been therefore deprecated.</p><p id="-8met8f_344">We would appreciate any feedback you have on this new approach in <a href="https://youtrack.jetbrains.com/issue/KT-68651/Compose-provide-a-single-place-in-extension-to-configure-all-compose-flags" id="-8met8f_350" data-external="true" rel="noopener noreferrer">YouTrack</a>.</p></section><section class="chapter"><h3 id="strong-skipping-mode-enabled-by-default" data-toc="strong-skipping-mode-enabled-by-default">Strong skipping mode enabled by default</h3><p id="-8met8f_351">Strong skipping mode for the Compose compiler is now enabled by default.</p><p id="-8met8f_352">Strong skipping mode is a Compose compiler configuration option that changes the rules for what composables can be skipped. With strong skipping mode enabled, composables with unstable parameters can now also be skipped. Strong skipping mode also automatically remembers lambdas used in composable functions, so you should no longer need to wrap your lambdas with <code class="code" id="-8met8f_354">remember</code> to avoid recomposition.</p><p id="-8met8f_353">For more details, see the <a href="https://developer.android.com/develop/ui/compose/performance/stability/strongskipping" id="-8met8f_355" data-external="true" rel="noopener noreferrer">strong skipping mode documentation</a>.</p></section><section class="chapter"><h3 id="composition-trace-markers-enabled-by-default" data-toc="composition-trace-markers-enabled-by-default">Composition trace markers enabled by default</h3><p id="-8met8f_356">The <code class="code" id="-8met8f_357">includeTraceMarkers</code> option is now set to <code class="code" id="-8met8f_358">true</code> by default in the Compose compiler Gradle plugin to match the default value in the compiler plugin. This allows you to see composable functions in the Android Studio system trace profiler. For details about composition tracing, see this <a href="https://medium.com/androiddevelopers/jetpack-compose-composition-tracing-9ec2b3aea535" id="-8met8f_359" data-external="true" rel="noopener noreferrer">Android Developers blog post</a>.</p></section><section class="chapter"><h3 id="non-skipping-group-optimizations" data-toc="non-skipping-group-optimizations">Non-skipping group optimizations</h3><p id="-8met8f_360">This release includes a new compiler option: when enabled, non-skippable and non-restartable composable functions will no longer generate a group around the body of the composable. This leads to fewer allocations and thus to improved performance. This option is experimental and disabled by default but can be enabled with the feature flag <code class="code" id="-8met8f_362">OptimizeNonSkippingGroups</code> as shown <a href="#new-way-to-configure-compiler-options" id="-8met8f_363">above</a>.</p><p id="-8met8f_361">This feature flag is now ready for wider testing. Any issues found when enabling the feature can be filed on the <a href="https://goo.gle/compose-feedback" id="-8met8f_364" data-external="true" rel="noopener noreferrer">Google issue tracker</a>.</p></section><section class="chapter"><h3 id="support-for-default-parameters-in-abstract-composable-functions" data-toc="support-for-default-parameters-in-abstract-composable-functions">Support for default parameters in abstract composable functions</h3><p id="-8met8f_365">You can now add default parameters to abstract composable functions.</p><p id="-8met8f_366">Previously, the Compose compiler would report an error when attempting to do this even though it is valid Kotlin code. We've now added support for this in the Compose compiler, and the restriction has been removed. This is especially useful for including default <code class="code" id="-8met8f_369">Modifier</code> values:</p><div class="code-block" data-lang="kotlin"> abstract class Composables { @Composable abstract fun Composable(modifier: Modifier = Modifier) } </div><p id="-8met8f_368">Default parameters for open composable functions are still restricted in 2.0.20. This restriction will be addressed in future releases.</p></section></section><section class="chapter"><h2 id="standard-library" data-toc="standard-library">Standard library</h2><p id="-8met8f_370">The standard library now supports universally unique identifiers as an Experimental feature and includes some changes to Base64 decoding.</p><section class="chapter"><h3 id="support-for-uuids-in-the-common-kotlin-standard-library" data-toc="support-for-uuids-in-the-common-kotlin-standard-library">Support for UUIDs in the common Kotlin standard library</h3><aside class="prompt" data-type="warning" data-title="" id="-8met8f_374"><p id="-8met8f_385">This feature is <a href="components-stability.html#stability-levels-explained" id="-8met8f_386">Experimental</a>. To opt in, use the <code class="code" id="-8met8f_387">@ExperimentalUuidApi</code> annotation or the compiler option <code class="code" id="-8met8f_388">-opt-in=kotlin.uuid.ExperimentalUuidApi</code>.</p></aside><p id="-8met8f_375">Kotlin 2.0.20 introduces a class for representing <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier" id="-8met8f_389" data-external="true" rel="noopener noreferrer">UUIDs (universally unique identifiers)</a> in the common Kotlin standard library to address the challenge of uniquely identifying items.</p><p id="-8met8f_376">Additionally, this feature provides APIs for the following UUID-related operations:</p><ul class="list _bullet" id="-8met8f_377"><li class="list__item" id="-8met8f_390"><p>Generating UUIDs.</p></li><li class="list__item" id="-8met8f_391"><p>Parsing UUIDs from and formatting them to their string representations.</p></li><li class="list__item" id="-8met8f_392"><p>Creating UUIDs from specified 128-bit values.</p></li><li class="list__item" id="-8met8f_393"><p>Accessing the 128 bits of a UUID.</p></li></ul><p id="-8met8f_378">The following code example demonstrates these operations:</p><div class="code-block" data-lang="kotlin"> // Constructs a byte array for UUID creation val byteArray = byteArrayOf( 0x55, 0x0E, 0x84.toByte(), 0x00, 0xE2.toByte(), 0x9B.toByte(), 0x41, 0xD4.toByte(), 0xA7.toByte(), 0x16, 0x44, 0x66, 0x55, 0x44, 0x00, 0x00 ) val uuid1 = Uuid.fromByteArray(byteArray) val uuid2 = Uuid.fromULongs(0x550E8400E29B41D4uL, 0xA716446655440000uL) val uuid3 = Uuid.parse("550e8400-e29b-41d4-a716-446655440000") println(uuid1) // 550e8400-e29b-41d4-a716-446655440000 println(uuid1 == uuid2) // true println(uuid2 == uuid3) // true // Accesses UUID bits val version = uuid1.toLongs { mostSignificantBits, _ -> ((mostSignificantBits shr 12) and 0xF).toInt() } println(version) // 4 // Generates a random UUID val randomUuid = Uuid.random() println(uuid1 == randomUuid) // false </div><p id="-8met8f_380">To maintain compatibility with APIs that use <code class="code" id="-8met8f_394">java.util.UUID</code>, there are two extension functions in Kotlin/JVM for converting between <code class="code" id="-8met8f_395">java.util.UUID</code> and <code class="code" id="-8met8f_396">kotlin.uuid.Uuid</code>: <code class="code" id="-8met8f_397">.toJavaUuid()</code> and <code class="code" id="-8met8f_398">.toKotlinUuid()</code>. For example:</p><div class="code-block" data-lang="kotlin"> val kotlinUuid = Uuid.parseHex("550e8400e29b41d4a716446655440000") // Converts Kotlin UUID to java.util.UUID val javaUuid = kotlinUuid.toJavaUuid() val javaUuid = java.util.UUID.fromString("550e8400-e29b-41d4-a716-446655440000") // Converts Java UUID to kotlin.uuid.Uuid val kotlinUuid = javaUuid.toKotlinUuid() </div><p id="-8met8f_382">This feature and the provided APIs simplify multiplatform software development by allowing code sharing among multiple platforms. UUIDs are also ideal in environments where generating unique identifiers is difficult.</p><p id="-8met8f_383">Some example use cases involving UUIDs include:</p><ul class="list _bullet" id="-8met8f_384"><li class="list__item" id="-8met8f_399"><p>Assigning unique IDs to database records.</p></li><li class="list__item" id="-8met8f_400"><p>Generating web session identifiers.</p></li><li class="list__item" id="-8met8f_401"><p>Any scenario requiring unique identification or tracking.</p></li></ul></section><section class="chapter"><h3 id="support-for-minlength-in-hexformat" data-toc="support-for-minlength-in-hexformat">Support for minLength in HexFormat</h3><aside class="prompt" data-type="warning" data-title="" id="-8met8f_402"><p id="-8met8f_406">The <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/-hex-format/" id="-8met8f_407" data-external="true" rel="noopener noreferrer"><code class="code" id="-8met8f_411">HexFormat</code></a> class and its properties are <a href="components-stability.html#stability-levels-explained" id="-8met8f_408">Experimental</a>. To opt in, use the <code class="code" id="-8met8f_409">@OptIn(ExperimentalStdlibApi::class)</code> annotation or the compiler option <code class="code" id="-8met8f_410">-opt-in=kotlin.ExperimentalStdlibApi</code>.</p></aside><p id="-8met8f_403">Kotlin 2.0.20 adds a new <code class="code" id="-8met8f_412">minLength</code> property to the <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/-hex-format/-number-hex-format/" id="-8met8f_413" data-external="true" rel="noopener noreferrer"><code class="code" id="-8met8f_416">NumberHexFormat</code></a> class, accessed through <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/-hex-format/number.html" id="-8met8f_414" data-external="true" rel="noopener noreferrer"><code class="code" id="-8met8f_417">HexFormat.number</code></a>. This property lets you specify the minimum number of digits in hexadecimal representations of numeric values, enabling padding with zeros to meet the required length. Additionally, leading zeros can be trimmed using the <code class="code" id="-8met8f_415">removeLeadingZeros</code> property:</p><div class="code-block" data-lang="kotlin" data-runnable="true" data-min-compiler-version="2.0" id="kotlin-2-0-20-minlength-hexformat"> fun main() { println(93.toHexString(HexFormat { number.minLength = 4 number.removeLeadingZeros = true })) // "005d" } </div><p id="-8met8f_405">The <code class="code" id="-8met8f_418">minLength</code> property does not affect parsing. However, parsing now allows hex strings to have more digits than the type's width if the extra leading digits are zeros.</p></section><section class="chapter"><h3 id="changes-to-the-base64-s-decoder-behavior" data-toc="changes-to-the-base64-s-decoder-behavior">Changes to the Base64's decoder behavior</h3><aside class="prompt" data-type="warning" data-title="" id="-8met8f_419"><p id="-8met8f_424">The <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io.encoding/-base64/" id="-8met8f_425" data-external="true" rel="noopener noreferrer"><code class="code" id="-8met8f_429">Base64</code> class</a> and its related features are <a href="components-stability.html#stability-levels-explained" id="-8met8f_426">Experimental</a>. To opt in, use the <code class="code" id="-8met8f_427">@OptIn(ExperimentalEncodingApi::class)</code> annotation or the compiler option <code class="code" id="-8met8f_428">-opt-in=kotlin.io.encoding.ExperimentalEncodingApi</code>.</p></aside><p id="-8met8f_420">Two changes were introduced to the Base64 decoder's behavior in Kotlin 2.0.20:</p><ul class="list _bullet" id="-8met8f_421"><li class="list__item" id="-8met8f_430"><p><a href="#the-base64-decoder-now-requires-padding" id="-8met8f_432">The Base64 decoder now requires padding</a></p></li><li class="list__item" id="-8met8f_431"><p><a href="#withpadding-function-for-padding-configuration" id="-8met8f_433">A <code class="code" id="-8met8f_434">withPadding</code> function has been added for padding configuration</a></p></li></ul><section class="chapter"><h4 id="the-base64-decoder-now-requires-padding" data-toc="the-base64-decoder-now-requires-padding">The Base64 decoder now requires padding</h4><p id="-8met8f_435">The Base64 encoder now adds padding by default, and the decoder requires padding and prohibits non-zero pad bits when decoding.</p></section><section class="chapter"><h4 id="withpadding-function-for-padding-configuration" data-toc="withpadding-function-for-padding-configuration">withPadding function for padding configuration</h4><p id="-8met8f_436">A new <code class="code" id="-8met8f_442">.withPadding()</code> function has been introduced to give users control over the padding behavior of Base64 encoding and decoding:</p><div class="code-block" data-lang="kotlin"> val base64 = Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT_OPTIONAL) </div><p id="-8met8f_438">This function enables the creation of <code class="code" id="-8met8f_443">Base64</code> instances with different padding options:</p><div class="table-wrapper"><table class="wide" id="-8met8f_439"><thead><tr class="ijRowHead" id="-8met8f_444"><th id="-8met8f_449"><p><code class="code" id="-8met8f_452">PaddingOption</code></p></th><th id="-8met8f_450"><p>On encode</p></th><th id="-8met8f_451"><p>On decode</p></th></tr></thead><tbody><tr id="-8met8f_445"><td id="-8met8f_453"><p><code class="code" id="-8met8f_456">PRESENT</code></p></td><td id="-8met8f_454"><p>Add padding</p></td><td id="-8met8f_455"><p>Padding is required</p></td></tr><tr id="-8met8f_446"><td id="-8met8f_457"><p><code class="code" id="-8met8f_460">ABSENT</code></p></td><td id="-8met8f_458"><p>Omit padding</p></td><td id="-8met8f_459"><p>No padding allowed</p></td></tr><tr id="-8met8f_447"><td id="-8met8f_461"><p><code class="code" id="-8met8f_464">PRESENT_OPTIONAL</code></p></td><td id="-8met8f_462"><p>Add padding</p></td><td id="-8met8f_463"><p>Padding is optional</p></td></tr><tr id="-8met8f_448"><td id="-8met8f_465"><p><code class="code" id="-8met8f_468">ABSENT_OPTIONAL</code></p></td><td id="-8met8f_466"><p>Omit padding</p></td><td id="-8met8f_467"><p>Padding is optional</p></td></tr></tbody></table></div><p id="-8met8f_440">You can create <code class="code" id="-8met8f_469">Base64</code> instances with different padding options and use them to encode and decode data:</p><div class="code-block" data-lang="kotlin" data-runnable="true" data-min-compiler-version="2.0" id="kotlin-2-0-20-base64-decoder"> import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi @OptIn(ExperimentalEncodingApi::class) fun main() { // Example data to encode val data = "fooba".toByteArray() // Creates a Base64 instance with URL-safe alphabet and PRESENT padding val base64Present = Base64.UrlSafe.withPadding(Base64.PaddingOption.PRESENT) val encodedDataPresent = base64Present.encode(data) println("Encoded data with PRESENT padding: $encodedDataPresent") // Encoded data with PRESENT padding: Zm9vYmE= // Creates a Base64 instance with URL-safe alphabet and ABSENT padding val base64Absent = Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT) val encodedDataAbsent = base64Absent.encode(data) println("Encoded data with ABSENT padding: $encodedDataAbsent") // Encoded data with ABSENT padding: Zm9vYmE // Decodes the data back val decodedDataPresent = base64Present.decode(encodedDataPresent) println("Decoded data with PRESENT padding: ${String(decodedDataPresent)}") // Decoded data with PRESENT padding: fooba val decodedDataAbsent = base64Absent.decode(encodedDataAbsent) println("Decoded data with ABSENT padding: ${String(decodedDataAbsent)}") // Decoded data with ABSENT padding: fooba } </div></section></section></section><section class="chapter"><h2 id="documentation-updates" data-toc="documentation-updates">Documentation updates</h2><p id="-8met8f_470">The Kotlin documentation has received some notable changes:</p><ul class="list _bullet" id="-8met8f_471"><li class="list__item" id="-8met8f_472"><p>Improved <a href="standard-input.html" id="-8met8f_478">Standard input page</a> - Learn how to use Java Scanner and <code class="code" id="-8met8f_479">readln()</code>.</p></li><li class="list__item" id="-8met8f_473"><p>Improved <a href="k2-compiler-migration-guide.html" id="-8met8f_480">K2 compiler migration guide</a> - Learn about performance improvements, compatibility with Kotlin libraries and what to do with your custom compiler plugins.</p></li><li class="list__item" id="-8met8f_474"><p>Improved <a href="exceptions.html" id="-8met8f_481">Exceptions page</a> - Learn about exceptions, how to throw and catch them.</p></li><li class="list__item" id="-8met8f_475"><p>Improved <a href="jvm-test-using-junit.html" id="-8met8f_482">Test code using JUnit in JVM - tutorial</a> - Learn how to create tests using JUnit.</p></li><li class="list__item" id="-8met8f_476"><p>Improved <a href="native-objc-interop.html" id="-8met8f_483">Interoperability with Swift/Objective-C page</a> - Learn how to use Kotlin declarations in Swift/Objective-C code and Objective-C declarations in Kotlin code.</p></li><li class="list__item" id="-8met8f_477"><p>Improved <a href="native-spm.html" id="-8met8f_484">Swift package export setup page</a> - Learn how to set up Kotlin/Native output that can be consumed by a Swift package manager dependency.</p></li></ul></section><section class="chapter"><h2 id="install-kotlin-2-0-20" data-toc="install-kotlin-2-0-20">Install Kotlin 2.0.20</h2><p id="-8met8f_485">Starting from IntelliJ IDEA 2023.3 and Android Studio Iguana (2023.2.1) Canary 15, the Kotlin plugin is distributed as a bundled plugin included in your IDE. This means that you can't install the plugin from JetBrains Marketplace anymore.</p><p id="-8met8f_486">To update to the new Kotlin version, <a href="releases.html#update-to-a-new-kotlin-version" id="-8met8f_487">change the Kotlin version</a> to 2.0.20 in your build scripts.</p></section><div class="last-modified">Last modified: 29 October 2024</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="competitive-programming.html" class="navigation-links__prev">Kotlin for competitive programming</a><a href="whatsnew20.html" class="navigation-links__next">What's new in Kotlin 2.0.0</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="static/v3/app.js"></script></body></html>