What's new in Kotlin 1.3
Released: 29 October 2018
Coroutines release
After some long and extensive battle testing, coroutines are now released! It means that from Kotlin 1.3 the language support and the API are fully stable. Check out the new coroutines overview page.
Kotlin 1.3 introduces callable references on suspend-functions and support of coroutines in the reflection API.
Kotlin/Native
Kotlin 1.3 continues to improve and polish the Native target. See the Kotlin/Native overview for details.
Multiplatform projects
In 1.3, we've completely reworked the model of multiplatform projects in order to improve expressiveness and flexibility, and to make sharing common code easier. Also, Kotlin/Native is now supported as one of the targets!
The key differences to the old model are:
In the old model, common and platform-specific code needed to be placed in separate modules, linked by
expectedBy
dependencies. Now, common and platform-specific code is placed in different source roots of the same module, making projects easier to configure.There is now a large number of preset platform configurations for different supported platforms.
The dependencies configuration has been changed; dependencies are now specified separately for each source root.
Source sets can now be shared between an arbitrary subset of platforms (for example, in a module that targets JS, Android and iOS, you can have a source set that is shared only between Android and iOS).
Publishing multiplatform libraries is now supported.
For more information, please refer to the multiplatform programming documentation.
Contracts
The Kotlin compiler does extensive static analysis to provide warnings and reduce boilerplate. One of the most notable features is smartcasts — with the ability to perform a cast automatically based on the performed type checks:
However, as soon as these checks are extracted in a separate function, all the smartcasts immediately disappear:
To improve the behavior in such cases, Kotlin 1.3 introduces experimental mechanism called contracts.
Contracts allow a function to explicitly describe its behavior in a way which is understood by the compiler. Currently, two wide classes of cases are supported:
Improving smartcasts analysis by declaring the relation between a function's call outcome and the passed arguments values:
Improving the variable initialization analysis in the presence of higher-order functions:
Contracts in stdlib
stdlib
already makes use of contracts, which leads to improvements in the analyses described above. This part of contracts is stable, meaning that you can benefit from the improved analysis right now without any additional opt-ins:
Custom contracts
It is possible to declare contracts for your own functions, but this feature is experimental, as the current syntax is in a state of early prototype and will most probably be changed. Also please note that currently the Kotlin compiler does not verify contracts, so it's the responsibility of the programmer to write correct and sound contracts.
Custom contracts are introduced by a call to contract
stdlib function, which provides DSL scope:
See the details on the syntax as well as the compatibility notice in the KEEP.
Capturing when subject in a variable
In Kotlin 1.3, it is now possible to capture the when
subject into a variable:
While it was already possible to extract this variable just before when
, val
in when
has its scope properly restricted to the body of when
, and so preventing namespace pollution. See the full documentation on when
here.
@JvmStatic and @JvmField in companions of interfaces
With Kotlin 1.3, it is possible to mark members of a companion
object of interfaces with annotations @JvmStatic
and @JvmField
. In the classfile, such members will be lifted to the corresponding interface and marked as static
.
For example, the following Kotlin code:
It is equivalent to this Java code:
Nested declarations in annotation classes
In Kotlin 1.3, it is possible for annotations to have nested classes, interfaces, objects, and companions:
Parameterless main
By convention, the entry point of a Kotlin program is a function with a signature like main(args: Array<String>)
, where args
represent the command-line arguments passed to the program. However, not every application supports command-line arguments, so this parameter often ends up not being used.
Kotlin 1.3 introduced a simpler form of main
which takes no parameters. Now "Hello, World" in Kotlin is 19 characters shorter!
Functions with big arity
In Kotlin, functional types are represented as generic classes taking a different number of parameters: Function0<R>
, Function1<P0, R>
, Function2<P0, P1, R>
, ... This approach has a problem in that this list is finite, and it currently ends with Function22
.
Kotlin 1.3 relaxes this limitation and adds support for functions with bigger arity:
Progressive mode
Kotlin cares a lot about stability and backward compatibility of code: Kotlin compatibility policy says that breaking changes (e.g., a change which makes the code that used to compile fine, not compile anymore) can be introduced only in the major releases (1.2, 1.3, etc.).
We believe that a lot of users could use a much faster cycle where critical compiler bug fixes arrive immediately, making the code more safe and correct. So, Kotlin 1.3 introduces the progressive compiler mode, which can be enabled by passing the argument -progressive
to the compiler.
In the progressive mode, some fixes in language semantics can arrive immediately. All these fixes have two important properties:
They preserve backward compatibility of source code with older compilers, meaning that all the code which is compilable by the progressive compiler will be compiled fine by non-progressive one.
They only make code safer in some sense — e.g., some unsound smartcast can be forbidden, behavior of the generated code may be changed to be more predictable/stable, and so on.
Enabling the progressive mode can require you to rewrite some of your code, but it shouldn't be too much — all the fixes enabled under progressive are carefully handpicked, reviewed, and provided with tooling migration assistance. We expect that the progressive mode will be a nice choice for any actively maintained codebases which are updated to the latest language versions quickly.
Inline classes
Kotlin 1.3 introduces a new kind of declaration — inline class
. Inline classes can be viewed as a restricted version of the usual classes, in particular, inline classes must have exactly one property:
The Kotlin compiler will use this restriction to aggressively optimize runtime representation of inline classes and substitute their instances with the value of the underlying property where possible removing constructor calls, GC pressure, and enabling other optimizations:
See reference for inline classes for details.
Unsigned integers
Kotlin 1.3 introduces unsigned integer types:
kotlin.UByte
: an unsigned 8-bit integer, ranges from 0 to 255kotlin.UShort
: an unsigned 16-bit integer, ranges from 0 to 65535kotlin.UInt
: an unsigned 32-bit integer, ranges from 0 to 2^32 - 1kotlin.ULong
: an unsigned 64-bit integer, ranges from 0 to 2^64 - 1
Most of the functionality of signed types are supported for unsigned counterparts too:
See reference for details.
@JvmDefault
Kotlin targets a wide range of the Java versions, including Java 6 and Java 7, where default methods in the interfaces are not allowed. For your convenience, the Kotlin compiler works around that limitation, but this workaround isn't compatible with the default
methods, introduced in Java 8.
This could be an issue for Java-interoperability, so Kotlin 1.3 introduces the @JvmDefault
annotation. Methods annotated with this annotation will be generated as default
methods for JVM:
Standard library
Multiplatform random
Prior to Kotlin 1.3, there was no uniform way to generate random numbers on all platforms — we had to resort to platform-specific solutions like java.util.Random
on JVM. This release fixes this issue by introducing the class kotlin.random.Random
, which is available on all platforms:
isNullOrEmpty and orEmpty extensions
isNullOrEmpty
and orEmpty
extensions for some types are already present in stdlib. 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
. Kotlin 1.3 provides similar extensions on collections, maps, and arrays of objects.
Copy elements between two existing arrays
The array.copyInto(targetArray, targetOffset, startIndex, endIndex)
functions for the existing array types, including the unsigned arrays, make it easier to implement array-based containers in pure Kotlin.
associateWith
It is quite a common situation to have a list of keys and want to build a map by associating each of these keys with some value. It was possible to do it before with the associate { it to getValue(it) }
function, but now we're introducing a more efficient and easy to explore alternative: keys.associateWith { getValue(it) }
.
ifEmpty and ifBlank functions
Collections, maps, object arrays, char sequences, and sequences now have an ifEmpty
function, which allows specifying a fallback value that will be used instead of the receiver if it is empty:
Char sequences and strings in addition have an ifBlank
extension that does the same thing as ifEmpty
but checks for a string being all whitespace instead of empty.
Sealed classes in reflection
We've added a new API to kotlin-reflect
that can be used to enumerate all the direct subtypes of a sealed
class, namely KClass.sealedSubclasses
.
Smaller changes
Boolean
type now has companion.Any?.hashCode()
extension that returns 0 fornull
.Char
now providesMIN_VALUE
andMAX_VALUE
constants.SIZE_BYTES
andSIZE_BITS
constants in primitive type companions.
Tooling
Code style support in IDE
Kotlin 1.3 introduces support for the recommended code style in IntelliJ IDEA. Check out this page for the migration guidelines.
kotlinx.serialization
kotlinx.serialization is a library which provides multiplatform support for (de)serializing objects in Kotlin. Previously, it was a separate project, but since Kotlin 1.3, it ships with the Kotlin compiler distribution on par with the other compiler plugins. The main difference is that you don't need to manually watch out for the Serialization IDE Plugin being compatible with the Kotlin IDE plugin version you're using: now the Kotlin IDE plugin already includes serialization!
See here for details.
Scripting update
Kotlin 1.3 continues to evolve and improve scripting API, introducing some experimental support for scripts customization, such as adding external properties, providing static or dynamic dependencies, and so on.
For additional details, please consult the KEEP-75.
Scratches support
Kotlin 1.3 introduces support for runnable Kotlin scratch files. Scratch file is a kotlin script file with the .kts extension that you can run and get evaluation results directly in the editor.
Consult the general Scratches documentation for details.