What’s new in Kotlin 1.4?

Kotlin 1.4.0, a sequel to 1.3.x, was released on August 17, 2020, and it ships with some improvements I find really cool. According to Jetbrains, Kotlin 1.4.0 is built with a focus on quality and performance. Let’s take a look at some of these cool features.

Language features and improvements

These include some of the language features added or improved upon.

Mixing named and positional arguments

In Kotlin 1.3, mixing named and positional arguments is not allowed, meaning you had to place arguments without names (i.e positional arguments) before the first named arguments, otherwise it throws an error as seen below.

fun main() {
    mixedArg(arg1 = "Hello", "Argument 2", "Argument 3") //Error: Mixing named and positioned arguments is not allowed

fun mixedArg(arg1: String, arg2: String, arg3: String) {

In Kotlin 1.4, the limitation is gone, you can now mix positional and named arguments, you can specify a name for an argument in the middle of a set of positional arguments, as long as the order is kept.

fun main() {
    mixedArg(arg1 = "Hello", arg2 = "Argument 2", "Argument 3")

fun mixedArg(arg1: String, arg2: String, arg3: String) {

Trailing comma

In Kotlin 1.4, you can now add a trailing comma to your parameter lists, arguments and basic lists. With this you can easily change order or rearrange items without having to worry about removing or adding commas. You can easily swap lines in a multi-line syntax for parameters or values.

val names = listOf(
    "Doe", //Trailing comma allowed

Using break and continue inside when expressions included in loops

So previously in Kotlin 1.3, you couldn’t use break and continue statements inside when expression included in a loop. The reason for this is that those keywords were reserved for fall-through behaviour in when expressions. So to get them to function, you had to label them, which became cumbersome (and your code looks kinda ugly?). Take a look at the code for better understanding.

fun sample(list: List<Int>) {
    loop@ for (l in list) {
        when (l) {
            3, 20 -> continue@loop
            50 -> break@loop
            else -> println(l)

In Kotlin 1.4, you can freely use break and continue without labels in such when expressions, and they behave as expected by terminating the nearest enclosing loop or proceeding to its next step.

fun sample(list: List<Int>) {
    for (l in list) {
        when (l) {
            3, 20 -> continue
            50 -> break
            else -> println(l)

New compiler

The new Kotlin compiler is faster and has new, more powerful type inference algorithm enabled by default. Below are some of the most noticeable improvements in the new algorithm.

More cases where type is inferred automatically

The new inference algorithm infers types for many cases where the old algorithm required you to specify them explicitly. In the example below, the type of the lambda parameter it is automatically and correctly inferred to String?.

val rulesMap: Map<String, (String?) -> Boolean> = mapOf(
        "weak" to { it != null },
        "strong" to { !it.isNullOrBlank() }

In Kotlin 1.3, you would need to explicitly introduce a lambda parameter to make it to work.

Smart casts for a lambda’s last expression

In Kotlin 1.3, the last expression inside a lambda wasn’t smart cast unless you specified the expected type. Therefore in the example below, Kotlin 1.3 infers result to be of type String?:

val result = run {
    var str: String? = "Hello"
    if (str == null) {
        str = "test"
    str //At this point, the Kotlin compiler knows str is no more null

Looking at the code, getting to the last expression in the lambda, the Kotlin compiler knows str is no more null, as the check above it should have been fulfilled. In Kotlin 1.4, thanks to the new inference algorithm, the type of the result variable becomes String, with no explicit casts needed.

This definitely is one of my favourite features. Kudos to the Jetbrains team!

Standard Library

The Kotlin standard library in 1.4 also got some significant changes, below are some of them.

New functions for arrays and collections

– Collections

In Kotlin 1.4, the standard library includes a number of useful functions for working with collections. Below are some of them:

  • setOfNotNull(), which creates a set consisting of all the non-null items in a given argument.
val set = setOfNotNull(null, null, 2, 40, 30, null)
print(set) //Prints [2, 40, 30]
  • shuffled(), which shuffles a collection of items.
val numbers = (0 until 20).asSequence()
val result = numbers.shuffled().take(5)
print(result.toList()) //Prints five random numbers between 0 and 20
  • *Indexed() counterparts for onEach() and flatMap(). This means with onEachIndexed() and flatMapIndexed(), you can have the element index as a parameter as well.
listOf("hello", "world").onEachIndexed { index, item ->
    println("$index : $item")
  • *orNull() counterparts for random(), reduce() and reduceIndexed(). Meaning randomOrNull(), reduceOrNull() and reducedIndexedOrNull() return null on empty collections.
val empty = emptyList<Int>()
empty.reduceOrNull { a, b -> a + b  }
  • removeFirst() and removeLast() functions for removing elements from a mutable list, and *orNull() counterparts of these functions as well.
val list = mutableListOf(1, 2, 3, 4)
print(list) //Prints [2, 3]
– Arrays

Just like collections, arrays got some new functions as well. Below are some of them:

  • shuffle() randomises the array elements.
  • onEach() performs the given action on each array element and returns the array itself.
  • reverse() for array subranges reverses the order of the elements in the subrange.
  • sortDescending() for array subranges sorts the elements in the subrange in descending order.
  • sort() and sortWith() for array subranges.

Below is a block of code showing some of these functions in use:

var language = ""
val letters = arrayOf("k", "o", "t", "l", "i", "n")
letters.onEach { language += it }
println(language) // "kotlin"

letters.reverse(0, 3)
letters.sortDescending(2, 5)

There are many more other functions added to Collections and Arrays, including the introduction of the ArrayDeque class, an implementation of a double-ended queue. You can check out the full documentation for more insight into these.

The updates in Kotlin 1.4 are not limited to those mentioned here in this article, there are many more, including deprecations, functions for bit operations, delegated properties improvements, functions for string manipulations and so on. Feel free to check out Jetbrain’s documentation on Kotlin 1.4 updates for more.