Changes in Newer Kotlin Versions

Kotlin keeps changing.

Generally newer versions of Kotlin are backwards-compatible with older ones. So, when we switch to a newer version of Kotlin (say, in build.gradle of an Android app project), most of our existing code should still work.

However, newer Kotlin versions offer new features that we can use in our development.

In this chapter, we will look at changes in the two newest Kotlin versions: 1.3 and 1.4.

Version 1.3

In November 2018, JetBrains released Kotlin 1.3. This was a relatively small update over Kotlin 1.2, but it did add a few new features of note. However, even today, some of those features are still marked as pre-release — not everything in a stable Kotlin version is stable.

With that in mind, here are some of the changes introduced in Kotlin 1.3.

when() and Subject Variables

As we saw earlier in the book, you can use an expression in when() to provide the value for comparison in each of the branches:

  val thingy = "foo"

  when (thingy) {
    "foo" -> println("something")
    "bar" -> println("something else")
    else -> println("and now for something completely different")
  }

Starting with Kotlin 1.3, you can make that expression also be stored in a local variable:

fun thingyProvider() = "like, whatever"

fun main() {
  when (val thingy = thingyProvider()) {
    "foo" -> println("something")
    "bar" -> println("something else")
    else -> println("we received: $thingy")
  }
}

The variable’s scope is the when() itself, so you can reference it from the branches but not anywhere else.

This is particularly useful for else branches, such as for logging unexpected values.

JVM Interop for Interfaces

If you have a companion object for an interface, you have improved interoperability options with Java:

In both cases, from Java’s standpoint, these become static members of the interface.

So, suppose we had:

interface Something {
    companion object {
        @JvmField
        val tag: String = "Something!"

        @JvmStatic
        fun printMe() {
            println(tag)
        }
    }

    // TODO add rest of interface
}

From Java code, we can reference Something.tag and Something.printMe().

Inline Classes

Later in the book, we will explore inline functions. Simply put, a function marked with inline works like a regular function, but the compiler “inlines” its implementation at each place it is used.

Kotlin 1.3 added a related capability, called inline classes. This feature is still deemed to be in alpha state, but it offers some interesting possibilities. We will explore inline classes more later in the book.

Contracts

Earlier in the book, we covered smartcasts, where Kotlin will allow syntax that on the surface seems impossible because it infers that it is possible under the circumstances.

open class Animal

class Frog : Animal() {
  fun hop() = println("Hop!")
}

class Axolotl : Animal() {
  fun swim() = println("Swish!")
}

fun main() {
  val critter: Animal = Frog()

  when (critter) {
    is Frog -> critter.hop()
    is Axolotl -> critter.swim()
  }
}

Here, we can hop() and swim() on critter because the compiler knows more about the type of critter given the rules encoded in the when branches.

However, prior to Kotlin 1.3, there were only a few places where smartcasts could help. Kotlin 1.3 adds a system of “contracts” that allows this sort of behavior in more places… including in code written by you (though this part is experimental).

Tactically, it means that if you have Kotlin 1.2 or older code, when you move to Kotlin 1.3, you may find that your IDE or linter will report a bunch of places where you can remove manual casts, unnecessary null checks, and the like. These will reflect the contracts that were added to the Kotlin standard library.

Long-term, “custom contracts”, written by ordinary developers, will be an option. As noted above, this is considered experimental. This document outlines the approach.

Unsigned Integer Types

Kotlin 1.3 added support for unsigned numeric types, such as UInt and ULong.

Note, though, that these are still considered to be in beta form. Unless you have a specific reason to use them (e.g., simplify interoperability with some JNI code in Android), it is probably better to leave them alone for now.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.