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!
Note that all new functions are added to the standard library in the experimental state.
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.
Some basic useful members on KClass
no longer require a kotlin-reflect
dependency on the JVM:
import kotlin.reflect.cast
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val kClass = String::class
println(kClass.simpleName) // String
println(kClass.qualifiedName) // kotlin.String
println(kClass.isInstance("abc")) // true
println(kClass.isInstance(10)) // false
println(kClass.cast("abc")) // abc
}
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.
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.
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.
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:
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val deque = ArrayDeque(listOf(1, 2, 3))
deque.addFirst(0)
deque.addLast(4)
println(deque) // [0, 1, 2, 3, 4]
println(deque.first()) // 0
println(deque.last()) // 4
deque.removeFirst()
deque.removeLast()
println(deque) // [1, 2, 3]
}
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!
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:
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val needsZero = true
val initial = listOf(2, 6, 41)
val ints = buildList { // this: MutableList
if (needsZero) {
add(0)
}
initial.mapTo(this) { it + 1 }
}
println(ints) // [0, 3, 7, 42]
}
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.
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:
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val list = listOf(1, 2, 3)
println(list.randomOrNull()) // 2
println(list.reduceOrNull { a, b -> a + b }) // 6
val emptyList = emptyList()
println(emptyList.randomOrNull()) // null
println(emptyList.reduceOrNull { a, b -> a + b }) // null
}
If you use random()
or reduce()
, you’ll get an exception if the collection is empty.
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.
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val ints = (1..4).asSequence()
println(ints.fold(0) { acc, elem -> acc + elem }) // 10
val sequence = ints.scan(0) { acc, elem -> acc + elem }
println(sequence.toList()) // [0, 1, 3, 6, 10]
}
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:
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.
This release includes several improvements for Kotlin support in IntelliJ IDEA. Let’s walk through the most interesting ones.
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.
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.
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.
参考资料:
https://github.com/JetBrains/kotlin/blob/1.3.70/ChangeLog.md
https://blog.jetbrains.com/kotlin/2020/03/kotlin-1-3-70-released/