Kotlin
A concise multiplatform language developed by JetBrains
Kotlin 1.3.70 Released
Today we’re happy to present to you the latest version of Kotlin – 1.3.70.
This incremental release doesn’t provide any major new features. However, we’ve tried our best to improve the existing functionality, fix issues, and even add experimental things for you to try. Here are the highlights of Kotlin 1.3.70:
- New functions and classes for Kotlin collections in the standard library.
- Various improvements in the IntelliJ Kotlin plugin: improved
*.gradle.kts
support, testing, debugging, completion, and so on. - The Kotlin/JVM compiler now generates type annotations in the bytecode for Java 8 and later targets.
- Bundle optimizations, npm dependency declarations, and long-awaited new docs for Kotlin/JS.
- Faster compilation and debugging for Kotlin/Native.
- Improved support for scripting in the IDE and command-line tools.
You can find the complete list of changes in the change log. As always, we’d like to thank our external contributors.
Now let’s dive into the details!
Standard library changes
Note that all new functions are added to the standard library in the experimental state.
Extending StringBuilder in the common library
StringBuilder
was already present in the common standard library, in the kotlin.text
package. However, many important members were missing or were available only on the JVM. Now, all the JVM StringBuilder
functionality was added to the common expect class
with the corresponding implementations on different platforms. This means you can effectively use StringBuilder
from common code as all the necessary members are there.
Working with KClass
Some basic useful members on KClass
no longer require a kotlin-reflect
dependency on the JVM:
Before, you needed to provide a Kotlin reflection implementation at runtime to make it work. Now you can use these simple members without additional dependencies.
Renaming of experimental annotations (@Experimental and @UseExperimental)
As you may know, Kotlin has a built-in mechanism for using experimental features. It includes annotations from the standard library, which mark declarations that are either experimental themselves or use other experimental declarations. In previous versions, these were @UseExperimental
and @Experimental
.
We’ve decided to widen the scope of this mechanism, because something being in the experimental state is not the only reason to require consent for using APIs. For example, an API can be internal or have some restrictions. We have renamed the annotations to reflect this: the names @OptIn
and @RequiresOptIn
have replaced @UseExperimental
and @Experimental
, accordingly. The compiler argument -Xuse-experimental
has been renamed to -Xopt-in
. As for -Xexperimental
, we’re dropping it because how rarely it is used and how much it increases complexity. The old declarations @Experimental
and @UseExperimental
are still supported in 1.3.70, but will be dropped in 1.4.
Renaming of experimental time measurement API
Another renaming we’ve made concerns the Duration and Time measurement API. Clock
and ClockMark
have been renamed to TimeSource
and TimeMark
, accordingly. The previous names are kept as deprecated type aliases for now.
Double-ended queue implementation: ArrayDeque
We’re happy to add the implementation of the double-ended queue, the kotlin.collections.ArrayDeque
class, to the Kotlin standard library! The community has been asking for it for some time. Even though you could use the java.util.ArrayDeque
class from the Java standard library, there was no common implementation you could use for Kotlin/JS, Kotlin/Native and, most importantly, in common code. Now such an implementation is available, albeit in the experimental state.
A double-ended queue allows you to add/remove elements both to/from the beginning and the end of the queue in amortized constant time:
You can use a double-ended queue by default when you need a queue or a stack in your code.
The kotlin.collections.ArrayDeque
implementation uses a resizable array underneath: it stores the contents in a circular buffer, an Array
, and resizes this Array
only when it becomes full.
Conceptually, the implementation of ArrayDeque
is very similar to that of java.util.ArrayDeque
. Note, however, that it’s a different implementation and this new implementation will be used when you use this class for Kotlin/JVM. This differs from how it works with other collections: when you create an ArrayList
and compile it to JVM, the java.util.ArrayList
class is used under the hood. Unlike Java’s ArrayDeque
, which implements only the Collection
interface, Kotlin’s ArrayDeque
implements MutableList
. This means you can access all the elements by index, which is not possible in Java’s ArrayDeque
.
We’re releasing the ArrayDeque
class now in the experimental state, and are looking forward to your feedback!
Collection builders
Another important new functionality is builder functions for collections: buildList
, buildSet
, and buildMap
. You can use such a builder function to conveniently manipulate a mutable collection during the creation phase and get a read-only collection as a result:
These builder functions are implemented similarly to buildString
. buildList
takes a lambda with a receiver as an argument. An implicit “this” receiver inside the lambda has the type MutableList
, while buildList
returns a read-only List as a result.
It is often more readable and more effective in terms of performance to use the builder functions when you need to perform complicated manipulations, like using conditions, modifying several initial collections and merging the result, and so on. Note that these functions are also currently released in the experimental state.
reduceOrNull() and randomOrNull() counterparts
You know this convention in Kotlin: having a pair of functions, where the first one throws an exception if the operation isn’t possible, and the second one returns null
, like string.toInt()
and string.toIntOrNull()
. Now we’ve added new randomOrNull()
and reduceOrNull()
counterpart functions, following the same convention:
If you use random()
or reduce()
, you’ll get an exception if the collection is empty.
scan() functions
We’re adding a new set of functions for working with lists and sequences. They represent the concept of “scanning”; similar functions are already present in different libraries and languages.
scan()
is closely related to fold()
. Both scan()
and fold()
apply the given binary operation to the sequence of values, but differ in that scan()
returns the whole sequence of intermediate results, while fold()
returns only the final result.
Like fold
, scan
takes an initial accumulator value:
If your result type is the same as an element type and you can use the first element as an initial value, then you can use the reduce()
and scanReduce()
functions:
<img class=”alignnone size-full wp-image-7941″ src=”https://blog.jetbrains.com/wp-content/uploads/2020/02/kotlin-scanReduce.png” onmouseover=”this.src=’https://blog.jetbrains.com/wp-content/uploads/2020/02/kotlin-scanReduce.gif‘;”
onmouseout=”this.src=’https://blog.jetbrains.com/wp-content/uploads/2020/02/kotlin-scanReduce.png‘;” alt=”” />
Note that when you use scan()
and scanReduce()
on sequences, they return sequences as a result, which are lazy by nature. That means that the scanning process only starts when you ask data from the resulting sequence, specifically call a terminal operation (the one returning something other than another sequence), such as toList()
or max()
. When you use scan()
and scanReduce()
on lists, they return lists as a result.
Read this KEEP for more details.
IntelliJ IDEA support
This release includes several improvements for Kotlin support in IntelliJ IDEA. Let’s walk through the most interesting ones.
*.gradle.kts support
In 1.3.70, we worked to improved IntelliJ IDEA’s support for Gradle Kotlin DSL scripts (*.gradle.kts
files). As a result, the latest version of the Kotlin plugin demonstrates better performance in syntax highlighting, completion, search, and other aspects of working with Kotlin build scripts. A detailed review of improvements is provided in this blog post.
To enjoy all the changes and improvements, make sure to use IntelliJ IDEA 2019.2 or later with Gradle 6.0 or later.
Code completion
In 1.3.70, we’ve made noticeable improvements for Kotlin code completion in IntelliJ IDEA. Now, completion suggestions include functions declared in objects, including extension functions, object-level overrides, and even functions declared in nested objects.
We’ve also improved the machine learning model that sorts the completions list, and now, the most relevant options appear at the top.
New color schemes
To enable you to change the appearance of Kotlin code in the editor to your liking, we’ve added new customizable color schemes. In particular, now you can set your own color schemes for the suspend function calls and property declarations.
Debugging improvements
In previous versions, the Kotlin/Native debugger used to have a separate breakpoint type, confusing some users with an unclear choice like “which breakpoint type should I use here?”. Now the single breakpoint type Kotlin Line Breakpoint
works for both JVM and Native targets.
Kotlin/JS and Kotlin/Native tests
Test results for Kotlin/JS and Kotlin/Native are now displayed right in the IntelliJ IDEA, as it has always been for Kotlin/JVM tests. Besides, we’ve fixed test filtering for Kotlin/JS, so you can run individual tests, and Kotlin/Native tests targeting the iOS Simulator can finally also be launched directly by pressing the “Play” button.
Moreover, in IntelliJ IDEA Ultimate, you can start the Kotlin/JS debugging by clicking an icon near the test declaration.
<img class=”alignnone size-full wp-image-7988″ src=”https://blog.jetbrains.com/kotlin/files/2021/02/js-test-debug.png” onmouseover=”this.src=’https://blog.jetbrains.com/kotlin/files/2021/02/js-test-debug.gif‘;”
onmouseout=”this.src=’https://blog.jetbrains.com/kotlin/files/2021/02/js-test-debug.png‘;” alt=”” />
Alternatively, you can start debugging by manually choosing nodeRun
, nodeTest
, or browserTest
task in the Gradle tool window. For browserRun
, you can attach debugger after starting the development server using the Attach to Node.js/Chrome run configuration.
Other notable mentions
There are also other improvements in Kotlin support in IntelliJ IDEA. Here are some worth mentioning:
- Improved performance of file analysis and copy-paste action.
- New inspection on pointless unary operators. They may appear when splitting long expressions onto multiple lines:
- Numerous formatting improvements, including formatting of call chains, line breaks, spacing, and comments.
Kotlin/JVM
Kotlin now can generate type annotations in the JVM bytecode (target version 1.8+), so that they become available at runtime. This feature has been requested by the community for some time, because it makes usage of some existing Java libraries much easier, and it gives more power to those authoring new libraries.
In the following example, the @Foo
annotation on the String
type can be emitted to the bytecode and then used by the library code:
To emit the type annotation in the bytecode, follow these steps:
- Make sure that your declared annotation has a proper annotation target (Java’s
ElementType.TYPE_USE
or Kotlin’sAnnotationTarget.TYPE
) and retention (AnnotationRetention.RUNTIME
). - Compile both the annotation class declaration and the code using this annotation (class A in the example above) to JVM bytecode target version 1.8+. You can specify it with
-jvm-target=1.8
compiler option. - Compile the code using the annotation with the
-Xemit-jvm-type-annotations
compiler option.
Note that the type annotations from the standard library aren’t emitted in the bytecode for now because they are compiled with the target version 1.6.
So far, only the basic cases are supported:
- Type annotations on method parameters, method return types and property types;
- Invariant projections of type arguments, e.g.
Smth<@Ann Foo>
,Array<@Ann Foo>
.
In the future, we’re planning to support generating type annotations for covariant and contravariant type projections and annotations on inner types (such as Smth<@Ann Outer.Inner>
). We believe that the supported cases should already cover most real-life scenarios, but if you need type annotations for more complicated cases, please let us know.
Kotlin/JS
With Kotlin 1.3.70, the JavaScript target receives some significant optimizations in terms of bundle size, and adds a few quality-of-life changes to the way dependencies, resources, and tests are handled. We’re also happy to announce our documentation overhaul for the target.
Bundle optimizations
Starting with Kotlin 1.3.70, DCE (Dead Code Elimination) is now readily available through the org.jetbrains.kotlin.js
Gradle plugin and does not need to be manually added. It receives a set of new tasks that you can use to control the optimization and execution of your JS project.
During development, use browserDevelopmentRun
to start a non-optimized run of your application with the bundled development server, and use browserDevelopmentWebpack
to create a non-optimized bundle of your application in the build/distributions
folder. If you choose not to run any optimizations, your build times will be faster, but compiled programs will have larger file sizes.
When preparing for production, use browserProductionRun
to start an optimized run of your application, and use browserProductionWebpack
to generate an optimized bundle that is fit for deployment in production. Production builds take a little longer to compile, but end up with a significantly better-optimized bundle than their development counterparts:
Project | Development Build (gzip) | Production Build (gzip) |
---|---|---|
“Hello, World” | 963 KiB | 14 KiB |
Kotlin/React Example (“CodeQuiz”) | 4.1 MiB | 345 KiB |
While the previous Gradle tasks browserRun
(now an alias for browserDevelopmentRun
) and browserWebpack
(now an alias for browserProductionWebpack
) are currently still available, you should replace them with their newly added equivalents in any of your build scripts.
These Gradle tasks are available for projects using the multiplatform
or kotlin.js
Gradle plugins. For kotlin.js
, you can also use the shorter task aliases run
for browserDevelopmentRun
and build
for browserProductionWebpack
.
If you’d like to learn more about Dead Code Elimination in Kotlin/JS and how to configure it in a fine-grained manner, check out the documentation.
Automatic copying of resources
Build tasks that create bundles in the distributions folder (like browserProductionWebpack
) now also copy all assets from the resources
folder, so it is no longer required to manually copy images, stylesheets, and others to get a deployable set of artifacts.
Smoother support for npm dependency declarations
When using the Kotlin/JS Gradle plugin, it is now possible to declare npm dependencies inside the top-level dependencies block, instead of having to manually add a main source set. This means that the following snippet is now valid:
Experimental test debugging through Gradle
It is now possible to debug tests from within IntelliJ IDEA directly for the Kotlin/JS browser target. To debug a failing test, set a breakpoint in the IDE, and then either start the check
Gradle task in debug mode or use the gutter icons to debug a set of tests. Gradle will execute the test task for the platform, and the debugger will stop at a synthetic breakpoint. It will hit the breakpoint as soon as Gradle shows the browserTest
task in the console:
At this point, switch to the :browserTest
session in the debugger, and select the tab labelled “Debugger”. Click the “Resume Program” button to begin executing your tests:
Currently, this process is necessary to keep the debugger in IntelliJ IDEA in sync with the JS engine. We’re aware that this number of steps to start a debugging session is cumbersome, so keep a lookout in future versions for an even smoother debugging experience!
We also plan to provide the same experience for debugging Kotlin code that targets Node.js after resolving some remaining issues.
Improved documentation
With Kotlin 1.3.70, we have made some long-awaited changes to the documentation and tutorials for the JavaScript target, which hopefully makes it easier for people to get started with Kotlin/JS. We have refreshed the documentation and removed outdated materials. We’ve also added a new series of short-form tutorials to the website, which outline typical tasks that you might encounter when working with Kotlin/JS. They can help you get started with the Kotlin/JS Gradle plugin, and they cover typical tasks when building applications for the JavaScript target. The hands-on course “Building Web Applications with React and Kotlin/JS” has also been updated and can now teach you how to use the Kotlin/JS plugin for Gradle.
We hope that these changes will make it easier for you to get started and work with Kotlin/JS, especially if you want to try it for frontend development. We are committed to constantly improving the documentation and reference materials that help you learn Kotlin/JS. So, if you are still missing any essentials in the documentation or how-to’s, please let us know in the comments.
Kotlin/Native
Performance optimizations
In 1.3.70, we’re ready to show the first results of our work on optimizing the overall performance of the Kotlin/Native development process. Compile-time performance was one of the known weak points in Kotlin/Native development, so we’ve got two new features to decrease compilation times:
- Now the Kotlin/Native compiler runs directly from the Gradle daemon, so no time is spent on starting a new process and loading the compiler into it on each compilation.
- In the debug mode, the compiler caches project dependencies. Now the first compilation takes a bit longer, but the subsequent ones complete faster. Note that currently, this works only for iOS simulator (iosX64) and macOS (macosX64); we’re still working on this.
We have not forgotten about runtime and debug performance improvements either. In 1.3.70, we’ve cut some time spent on object allocation: now more objects are allocated on a stack (which is faster than heap allocation), and some singleton objects are created at the compile time. The debug process has become faster as well.
Support for multiple Kotlin frameworks in a single application
Previously, there was a known issue that an application could not use more than one dynamic Kotlin/Native framework because Obj-C classes defined in runtime were conflicting, coming from different instances of runtime. We’ve fixed this in 1.3.70, so now an application can use multiple Kotlin/Native frameworks. Any common dependencies are present in the frameworks under different Swift modules (and with different Obj-C prefixes).
Support for vector types
Starting with 1.3.70, Kotlin/Native supports SIMD types. This gives you many more third-party APIs available for using from Kotlin/Native, such as the popular Apple frameworks Accelerate and SpriteKit.
Scripting
Version 1.3.70 presents a set of improvements that provide a better experience of using Kotlin scripts with IntelliJ IDEA and Kotlin command-line tools.
Additionally, to help you become familiar with Kotlin scripting, we’ve prepared a project with examples. It contains examples of the standard scripts (*.main.kts
) and examples of the scripting API usage and custom script definitions. Please try it and share your feedback in our issue tracker.
kotlin-main-kts
support in compiler and IDE
In Kotlin 1.3, we introduced the kotlin-main-kts artifact which simplifies the creation and usage of the basic utility scripts. Now it is loaded by default by the Kotlin compiler and IntelliJ plugin, so that *.main.kts
scripts are supported out of the box. In particular, now you can use such scripts without adding kotlin-main-kts.jar
to the classpath.
In IntelliJ IDEA, this gives you highlighting and navigation, including resolve into dynamic dependencies, for *.main.kts
files, even outside the source directories.
We’ve also improved the performance of the compiled *.main.kts
scripts execution by enabling caching for them to make subsequent runs significantly faster.
Command-line tools
We’ve also extended the support for Kotlin scripting in the command line tools.
The kotlin
runner distributed with the Kotlin command-line compiler now supports script execution. To run a script with kotlin
, just pass the script file name to it:
Such a call will run the script in exactly the same way as if you called the compiler with -script
option. kotlin
recognizes .main.kts
scripts out of the box.
The kotlin
runner can also be used for expression evaluation. To evaluate an expression, pass it as a value of the –e/-expression
option:
You’ll get the result right away.
The same functionality is added to the Kotlin command-line compiler but with a different option name: -Xexpression
:
How to update
As always, you can try Kotlin online at play.kotl.in.
- In Gradle and Maven: Use
1.3.70
as the version for the compiler and the standard library. See the docs for Gradle and for Maven. - In IntelliJ IDEA and Android Studio: Update the Kotlin plugin to version 1.3.70. Use Tools | Kotlin | Configure Kotlin Plugin Updates and click the “Check again” button.
- In Eclipse: Install the plugin using the Marketplace.
- The command-line compiler can be downloaded from the Github release page.
If you run into any problems with the new release, you’re welcome to ask for help on the forums on Slack (get an invite here), or to report issues in our issue tracker.
Let’s Kotlin!
External contributors
We want to thank Fleshgrinder for his work on adding support for builder functions for collections, and adellibovi for adding reduceOrNull()
functions.
Thanks also to all of our external contributors whose pull requests were included in this release:
- pyos
- Steven Schäfer
- Toshiaki Kameyama
- Mark Punzalan
- Mads Ager
- Kristoffer Andersen
- Ivan Gavrilovic
- Jiaxiang Chen
- Kevin Bierhoff
- Alfredo Delli Bovi
- Tillmann Berg
- Victor Turansky
- Alexander Shustanov
- Leonardo Colman Lopes
- Juan Chen
- Jordan Demeulenaere
- Jim Sproc
- Burak Eregar
- Dmitriy Jarosh
- Dat Trieu
- Dmitry Borodin
- Efeturi Money
- Miguel Serra
- Fleshgrinder
- Jens Klingenberg
- Louis CAD