Kotlin 1.3.70 is here! As the release blog post says, there’s not much in the way of major landmark new features, but lots of incremental improvements and some interesting little quality-of-life stuff which I’m excited about. It's the first new language version since KotlinConf so it's cool to see some of the things JetBrains has been working on since then, even if a lot of the big-ticket stuff won't come until 1.4.
Before we jump in, a quick reminder for Multiplatform users: Since Kotlin/Native is still in Beta, binaries are not compatible across versions. This means that you shouldn't update your Multiplatform projects until all of your library dependencies have Kotlin 1.3.70-compatible versions. If you're a KaMPKit user, you can follow this issue to see when the libraries we use in that template are ready.
Here's a couple of items in the blog post I thought were noteworthy.
Renaming of experimental annotations
I've been using @Experimental
and @UseExperimental
for over a year now in Multiplatform Settings, and find it to be a really nice way for libraries to mark more volatile APIs. Simply create an annotation class MyExperimentalApi
and annotate it with @Experimental
, and then consumers of anything marked @MyExperimentalApi
will have to explicitly opt-in to their dependence on experimental behavior.
But I've also noticed some usage in the wild (including in JetBrains' own libraries) where it was more about marking internal APIs they didn't want other people depending on, rather than just APIs that might be unstable. So I'm not surprised that they're renaming these to @RequiresOptIn
and @OptIn
to broaden the usage.
A side note here since I've seen people miss this: A @RequiresOptIn
annotation deliberately leaks to callers so that they're forced to opt-in as well. If you don't want to do that (for instance, because the experimental usage is just an internal implementation detail), you should use @OptIn(MyExperimentalApi::class)
instead so you only need to annotate at the direct use-site. You can also pass opt-ins at the command-line or gradle level that apply application-wide.
Double-ended queue implementation: ArrayDeque
This is a neat one which I hadn't been tracking the development of. ArrayDeque
is a common default implementation for a Queue
or a Stack
on the JVM, but no such standard implementation existed on other platforms previously. That will be a welcome addition to people in need of these data structures in common code.
The fact that it's a new implementation even on the JVM rather than delegating to the Java one is interesting, and atypical for Kotlin. I guess the benefit is that it allows them to improve the API some, by allowing it to be treated as a List
which isn't possible for the Java implementation. On the other hand it makes it less useful for Java interop. Interestingly, they have not actually added Queue
and Stack
interfaces to kotlin.collections
, so for example you'd get stack behavior by calling addFirst()
and removeFirst()
rather than push()
and pop()
. I'll be curious to see if these differences from Java have impact on how people use Kotlin's ArrayDeque
.
IntelliJ IDEA support
Now, the completion suggestions include functions declared in objects, including extension functions, object-level overrides, and even functions declared in nested objects.
It's nice to see support improving here for this longstanding issue. The pattern of declaring extension functions in objects looks weird at first and doesn't feel all that idiomatic, but when combined with things like the @JvmStatic
annotation it's very helpful for maintaining clean interfaces for Java consumers. That means that libraries like KotlinPoet and OkHttp make heavy use of it, and so it's been a pain point that imports often need to be manually added from Kotlin.
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.
I am already super pleased about this.
Liquid error: internal
Also, it's not called out in the blog, but a related improvement here is that the iosX64Test
gradle task (and the equivalent for other simulator targets) is built-in now, so we no longer have to define it manually. I've lost count of how many times I've copy/pasted that gradle snippet.
New inspection on pointless unary operators.
This is a subtle gotcha that's good to have some tooling help for. If you're not careful when splitting up a long math expression, you might end up with code like this
val a = 2 + 3
+ 4
The compiler will see this as two lines, so a
will have the value 5, and not 9 as you might expect. It's a subtle consequence of a language without semicolons, since the parser has to infer where one expression ends and the next begins. Glad we'll have an inspection calling it out, but I'd also love to see a quick-fix to convert it to the form you probably meant:
val a = 2 + 3 +
4
When the plus sign is at the end of the line, the compiler realizes that the 4 on the next line is part of the same expression and so a
would be 9 as expected.
Kotlin/JS
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.
Been waiting on this one a while too. There were major updates to the JS gradle plugin API in 1.3.40 but updated documentation has been slow to arrive. Good to have it here now.
Kotlin/Native
Support for multiple Kotlin frameworks in a single application
People have been asking for this for a while. There are some limitations to how multiple-framework applications will work, because each framework will have its own versions of the Kotlin standard library and any other common dependencies. However it should make the iOS developer experience more natural in some cases, especially in large multimodule projects.
Lastly, I thought I’d mention one thing we didn’t see. Incremental experimental support has been increasing over the last few releases for “Hierarchical Multiplatform Projects” (HMPP). This is a new internal model used by the gradle plugin and the IDE to infer things about intermediate shared sources, which differ from traditional common sources in that they can access platform-specific APIs which exist in all platforms the source-set compiles to. An example is accessing iOS APIs from code that is shared between iosArm64 (for iOS devices) and iosX64 (for the iOS simulator). I was hoping to see some details about this in the 1.3.70 announcement, but I guess it’s not quite ready yet. (Hopefully, the bug I encountered right before the release didn’t throw too big a wrench in their plans). Looking forward to seeing what HMPP looks like in Kotlin 1.4 and beyond.
Cover image credit: JetBrains
Thanks to Justin Mancinelli, Sam Hill, Kevin Galligan, Erik Zambrano, Kevin Schildhorn, and Jeff Namnum for providing feedback on drafts of this post.
Top comments (0)