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( "John", "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 foronEach()
andflatMap()
. This means withonEachIndexed()
andflatMapIndexed()
, you can have the element index as a parameter as well.
listOf("hello", "world").onEachIndexed { index, item -> println("$index : $item") }
*orNull()
counterparts forrandom()
,reduce()
andreduceIndexed()
. MeaningrandomOrNull()
,reduceOrNull()
andreducedIndexedOrNull()
returnnull
on empty collections.
val empty = emptyList<Int>() empty.reduceOrNull { a, b -> a + b }
removeFirst()
andremoveLast()
functions for removing elements from a mutable list, and*orNull()
counterparts of these functions as well.
val list = mutableListOf(1, 2, 3, 4) list.removeFirst() list.removeLast() 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()
andsortWith()
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.shuffle() 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.