See what’s coming in Kotlin 1.3-M1

Posted on by

Today, after a long chain of incremental 1.2.X updates, it’s time to see what’s coming in Kotlin 1.3. We are happy to announce the first preview version of the new major release: Kotlin 1.3-M1.

Kotlin 1.3 brings many advancements including graduation of coroutines, new experimental unsigned arithmetic, and much more. Please read on for the details.

We’d like to thank our external contributors whose pull requests and commits were included in this release: Raluca Sauciuc, Toshiaki Kameyama, Leonardo Lopes, Jake Wharton, Jeff Wright, Lucas Smaira, Mon_chi, Nico Mandery, Oskar Drozda.

The complete list of changes in this release can be found in the changelog.

Coroutines are graduating to stable

Finally, coroutines will not be experimental anymore in 1.3. Both the syntax and the Standard Library APIs are stabilized and will remain backwards-compatible in the future.

Coroutines have been improved significantly since their introduction in 1.1. Notable features include:

KT-16908 Support callable references to suspending functions

KT-18559 Serializability of all coroutine-related classes

The core APIs have been simplified and moved out of the experimental packages.

We are also working on a multiplatform version of the coroutine APIs, including iOS support through Kotlin/Native.

Migration to new coroutines

As we’ve said earlier, all the packages with coroutine-related functions in Kotlin have dropped experimental from their package names, and buildSequence and buildIterator functions have moved to their permanent place in the kotlin.sequences package.

On the language level, there is still a suspend modifier to support coroutines, and all the rules for working with coroutines remain mostly the same as they were in the experimental version.

The graduated Continuation<T> interface is simplified. It has only one resumeWith(result: SuccessOrFailure<T>) member function (for details see the SuccessOrFailure section below). The resume(value: T) and resumeWithException(exception: Throwable) are now defined as extensions. This affects only a small minority of code that defines its own coroutine builders and leaves classic examples of wrapping callbacks into suspending functions mostly unchanged. For example, a definition of the suspending function await() for the class CompletableFuture<T> would still look like this:

suspend fun <T> CompletableFuture<T>.await(): T = suspendCoroutine { cont -> whenComplete { value, exception -> when { exception != null -> cont.resumeWithException(exception) else -> cont.resume(value) } } }

Graduated coroutines use a different binary interface (the major difference being the change of package names) and are not binary-compatible with experimental coroutines. To ensure a smooth migration, we’ll add a compatibility layer in 1.3 and the classes for experimental coroutines will still remain in the standard library. The compiled Kotlin/JVM code that uses experimental coroutines from 1.1 or 1.2 would still continue to work in Kotlin 1.3.

However, the Kotlin 1.3-M1 release does not provide any support for invoking experimental suspending functions from code that is compiled with the language version 1.3. In order to try graduated coroutines in Kotlin 1.3-M1 with libraries that expose suspending functions in their APIs, you will need to use versions of these libraries that are compiled for Kotlin 1.3. This is a temporary inconvenience that we’ll fix soon.

We will be providing a version of kotlinx.coroutines library with x.x.x-eap13 version that is built with Kotlin 1.3 and drops experimental from its package names for every x.x.x version that we release while Kotlin 1.3 is in the preview phase.

The IDE will assist you in migrating to new coroutines. We’ll improve the range of supported migration scenarios before 1.3.

New features

While the bigger new capabilities of 1.3 will be experimental (see below), we are releasing a number of smaller features for your convenience now.

Capturing when subject in a variable

The common case of capturing the subject expression of when in a variable is now supported in the language:

fun Request.getBody() = when (val response = executeRequest()) { is Success -> response.body is HttpError -> throw HttpException(response.status) }

This small addition makes the response variable properly scoped within when , and makes the logic flow in a more expression-like fashion.

This was one of the most popular feature requests in our issue tracker. Thanks to all voters for your feedback!

@JvmStatic and @JvmField in companions of interfaces

Your Kotlin interfaces can now expose static members (fields and methods) to Java clients. The syntax is similar to that for classes; a companion object of an interface can now have @JvmStatic or @JvmField inside:

interface Service { companion object { @JvmField val ID = 0xF0EF @JvmStatic fun create(): Service = ... } }

Note that @JvmField is an all-or-nothing annotation here: to use it on interface companion properties, all of them must be public final vals and all of them must be annotated.

To use the @JvmStatic annotation on interface companion members, the -jvm-target compiler option must be at least 1.8 .

Nested declarations in annotation classes

Before 1.3, annotation classes could not have bodies in Kotlin. This release relaxes this limitation, allowing them to have nested classes, interfaces, and objects including a companion object. So, for example, it becomes possible to nest one annotation into another:

annotation class Outer(val param: Inner) { annotation class Inner(val value: String) }

Since @JvmField and @JvmStatic are supported in interfaces, it is now possible to declare such members in annotation companion objects:

annotation class Annotation { companion object { @JvmField val timestamp = System.currentTimeMillis() } }

Functional types of higher arity

A function type can now have more than 22 parameters! We have pushed that limit to 255 parameters, which is the practical maximum number of parameters a method can have on the JVM. To find out how we’ve managed to do that without introducing another 233 classes, see the corresponding KEEP.

Experimental language features

As coroutines have demonstrated, providing early access to bigger features through making them experimental helps us gather invaluable feedback from the community. We’ll keep using this technique to make sure that Kotlin gets only battle-tested design choices that will last.

Kotlin 1.3 brings three exciting new features that will remain experimental for now. An explicit opt-in is required in your project in order to use them. Without an opt-in, usages of a feature will be marked as warnings or errors.

Inline classes

Inline classes allow wrapping a value of some type without creating an actual wrapper object.

inline class Name(internal val value: String)

When such a class is used, the compiler inlines its content and operations are performed on the wrapped value itself. Hence, this

val name: Name = Name("Mike") fun capitalize(name: Name): Name = Name(name.value.capitalize())

results in the same compiled code as this:

val name: String = "Mike" fun capitalize(name: String): String = name.capitalize()

Inline classes are similar to type aliases, but they are not assignment-compatible with the underlying value type, so you cannot assign a String to a variable of type Name , and vice versa.

Since inline classes have no identity, the === operator cannot be used on them.

There are still places where inline classes result in wrappers being created, similar to how Int can be primitive in some usages and boxed in others (“auto-boxing”):

val key: Any = Name("Mike") // boxing to actual Name wrapper val pair = Name("Mike") to 27 // Pair is a generic type, so Name is boxed here too

This feature can be enabled with the compiler option -XXLanguage:+InlineClasses .

See this KEEP for details. Be sure to read the “Limitations” section.

Unsigned integer types

The most obvious application of inline classes is unsigned integer types. The Standard Library now has UInt , ULong , UByte , and UShort defined as inline classes wrapping the corresponding signed counterparts. They define their own arithmetic operations that interpret the storage value as an unsigned integer.

fun main(args: Array<String>) { //sampleStart val operand1 = 42 val operand2 = 1000 * 100_000 val signed: Int = operand1 * operand2 val unsigned: UInt = operand1.toUInt() * operand2.toUInt() //sampleEnd println("Signed result: $signed, unsigned result: $unsigned") }

In addition to the new types, there are new language features to support them, which makes these types special.

Varargs of unsigned types are allowed, unlike other inline classes fun maxOf(vararg values: UInt): UInt { ... }

Unsigned number literals are provided to simplify initializing variables of unsigned types val uintMask = 0xFFFF_FFFFu val ulongUpperPartMask = 0xFFFF_FFFF_0000_0000uL

Constant values of unsigned types are allowed const val MAX_SIZE = 32768u

Note that 1.3-M1 does not yet allow complex const expressions of unsigned types:

const val MAX_SIZE_BITS = MAX_SIZE * 8u // Error in 1.3-M1

Such expressions will be supported a bit later.

Unsigned types are provided as an experimental API: you need to opt in for them, otherwise a warning will be issued at the use site. To opt in:

either annotate the code element that uses unsigned types with the @UseExperimental(ExperimentalUnsignedTypes::class) annotation

annotation or specify the -Xuse-experimental=kotlin.ExperimentalUnsignedTypes compiler option.

Additionally, if you are a library author and are going to use experimental unsigned types, we recommend that you annotate parts of your API with the @ExperimentalUnsignedTypes annotation. This will propagate the experimentality of unsigned types to your library’s users.

More details about unsigned types can be found in this KEEP.

Annotations for marking an experimental API and opting-in for it

The mechanism of experimental unsigned types opt-in described above is also available in a generic way to library authors. It allows you to provide an experimental API (i.e. one that is subject to future changes), which may break at any moment and require any usages to be fixed and recompiled.

Users of experimental APIs may opt in by agreeing essentially to the following: “I understand that I’m using an experimental declaration that may break at any time”. Without such explicit consent from the user, a warning or error will be reported on usages of experimental APIs.

The ultimate goal is to allow library authors to release APIs earlier and more frequently without a commitment to backward compatibility (source or binary).

Experimental API annotations are described in detail in this KEEP.

New Standard Library APIs

Now let’s look at what new APIs are available in the standard library in this release.

SuccessOrFailure

The inline class SuccessOrFailure is effectively a discriminated union between successful and failed outcomes of execution of a Kotlin function: Success T | Failure Throwable .

It has been introduced to allow for capturing the result of a function execution, whether successful or not, in order to process it at a later time.

import java.io.File typealias SuccessOrFailure<T> = Result<T> // SuccessOrFailure was renamed to Result in 1.3-RC fun readFileData(file: File): String = when { file.name.startsWith("a") -> throw Exception("No such file: $file") file.nameWithoutExtension.matches(Regex("\\d+")) -> file.nameWithoutExtension else -> "File $file content" } fun List<SuccessOrFailure<*>>.printResults() { forEach { r -> r.onSuccess { println("Success: $it") } r.onFailure { println("Failure: $it") } } } fun main(args: Array<String>) { //sampleStart val files = listOf(File("a.txt"), File("b.txt"), File("42.txt")) val contents: List<SuccessOrFailure<String>> = files.map { runCatching { readFileData(it) } } println("map successful items:") val upperCaseContents: List<SuccessOrFailure<String>> = contents.map { r -> r.map { it.toUpperCase() } } upperCaseContents.printResults() println() println("map successful items catching error from transform operation:") val intContents: List<SuccessOrFailure<Int>> = contents.map { r -> r.mapCatching { it.toInt() } } intContents.printResults() //sampleEnd }

The primary driver for introducing this class is the new Continuation interface, where we want to have a single resumeWith(result: SuccessOrFailure<T>) function instead of two, resume(T) and resumeWithException(Throwable) .

Note that the Kotlin style guide discourages using SuccessOrFailure type as a return type of Kotlin functions. This is similar to how users are encouraged to have suspend functions return some type instead of plain functions returning a Deferred of that type.

There are a few exceptions from this rule, which are explained in more detail in the proposal KEEP-127. Scroll down to the section on style and exceptions.

Multiplatform random number generator

While there’s no problem with generating random numbers in Kotlin/JVM (aside from ThreadLocalRandom being unavailable prior to JDK 7), no uniform API is yet available to do that in Kotlin for other platforms and, more importantly, in multiplatform code.

This release introduces a new common Random abstract class, which serves as a base class for random number generator implementations.

Note that if you just need a random number, there’s no need to inherit and implement this class. The default random number generator implementation is always at hand: it is the Random companion object. So getting a number is as easy as

import kotlin.random.* fun main(args: Array<String>) { val limit = 100 //sampleStart val number = Random.nextInt(limit) // number is in range [0, limit) //sampleEnd println(number) }

A repeatable random generator initialized with a particular seed can be obtained with the top-level function Random(seed)

import kotlin.random.* fun main(args: Array<String>) { //sampleStart val seriesRng = Random(42) val series = List(100) { seriesRng.nextDouble() } //sampleEnd println(series.take(5)) }

For details please refer to this KEEP-131.

Companion object for Boolean type

Basic types in Kotlin such as Int , Char , and String have a companion with some properties like MIN_VALUE .

Until recently, the Boolean type stood aside having no companion object.

Our community members have convinced us that there are use cases for having a Boolean companion even without any properties, and Leonardo Lopes has helped implement it.

New constants in companion objects of basic types

Existing companion objects of basic types have received these new properties:

Byte , Short , Int , Long , Char now have SIZE_BITS and SIZE_BYTES constants, telling how many bits or bytes a value of that type takes in binary form.

, , , , now have and constants, telling how many bits or bytes a value of that type takes in binary form. Char now has MIN_VALUE and MAX_VALUE constants equal to '\u0000' and '\uFFFF' , respectively.

isNullOrEmpty and orEmpty extensions

isNullOrEmpty and orEmpty extensions on nullable collection-like types are not new in the standard library. The first one returns true if the receiver is null or empty, and the second one falls back to an empty instance if the receiver is null .

This release improves the API’s consistency by providing an orEmpty() extension for sequences and an isNullOrEmpty extension for collections, maps, and arrays of objects.

Announcing breaking changes

Some bug fixes introduce changes that are formally breaking. To ensure smooth migration, we deprecate old behavior, report warnings and provide migration tools whenever possible, and always announce such changes in advance.

Note that this is merely an announcement, so some of the changes listed below are not implemented yet and will be implemented in the following milestones.

You can consult the changelog to find out which of these changes have made it into this release.

Compiler changes

KT-11567 Companion object instance field is no longer public when the companion object itself is not public

KT-16615 Initialization of properties without const modifier is changed so Java does not treat them like constants

modifier is changed so Java does not treat them like constants KT-21354 An array is captured before the for -loop where it is iterated, so its reassignment inside the loop does not affect the iteration

-loop where it is iterated, so its reassignment inside the loop does not affect the iteration KT-22275 The type of exceptions thrown from failed null checks is being unified to NullPointerException

KT-19532 Evaluation order of constructor arguments regarding <clinit> call is changed

Previously deprecated feature usages that are now prohibited:

KT-16681 val reassignment in its getter

KT-16310 Nested classes in enum entries

KT-19618 Data classes overriding copy with the generated method

with the generated method KT-17981 Inner classes inheriting Throwable that capture generic parameters from the outer class

that capture generic parameters from the outer class KT-21515 Accessible classes and interfaces nested in companion objects

KT-25333 Java static members accessible through supertypes of companion object

KT-23153 Non-constant vararg annotation parameters

KT-23277 Local annotation classes

KT-22517 Smart casts for local delegated properties

KT-24197 mod operator convention. Note that operator rem should be used instead of mod since Kotlin 1.1

operator convention. Note that operator should be used instead of since Kotlin 1.1 KT-20588, KT-20589 Passing single element to vararg in named form

KT-13762 Annotations with target EXPRESSION cannot have retention other than SOURCE

cannot have retention other than KT-22379 Smart cast is removed after while loops with break

KT-20830 Propagation of Java types nullability enhanced by annotations

KT-9580 setparam annotation target for a parameter declaration

Standard library changes

KT-21784 The org.jetbrains.annotations package is being removed from the standard library in the compiler distribution. Note that the maven standard library artifacts are not affected.

package is being removed from the standard library in the compiler distribution. Note that the maven standard library artifacts are not affected. KT-19489 Array.copyOfRange throws an exception when indices are out of bounds instead of enlarging the returned array

throws an exception when indices are out of bounds instead of enlarging the returned array KT-17176 Progressions of ints and longs with a step of Int.MIN_VALUE and Long.MIN_VALUE are outlawed and won’t be allowed to be instantiated

and are outlawed and won’t be allowed to be instantiated KT-16097 Check for index overflow in operations on very long sequences

KT-21049 Unify split by an empty match regex result across the platforms

Pre-release notes

Note that the backward compatibility guarantees do not cover pre-release versions: the features and API can change in subsequent releases. When we reach a final RC, all binaries produced by pre-release versions will be outlawed by the compiler, and you will be required to recompile everything that was compiled by 1.3‑Mx.

However, all the code compiled by 1.2.x and earlier releases will be perfectly fine then without recompilation.

How to Try It

In Maven/Gradle: Add http://dl.bintray.com/kotlin/kotlin-eap as a repository for the build script and your projects; use 1.3-M1 as the version number for the compiler plugin and the standard library.

In IntelliJ IDEA: Go to Tools → Kotlin → Configure Kotlin Plugin Updates, then select “Early Access Preview 1.3” in the Update channel drop-down list, and then click Check for updates.

The command-line compiler can be downloaded from the Github release page.

On try.kotlinlang.org: Use the drop-down list in the bottom right-hand corner to change the compiler version to 1.3‑M1.