What's new in Kotlin 1.5.0
Kotlin 1.5.0 introduces new language features, stable IR-based JVM compiler backend, performance improvements, and evolutionary changes such as stabilizing experimental features and deprecating outdated ones.
You can also find an overview of the changes in the release blog post.
Language features
Kotlin 1.5.0 brings stable versions of the new language features presented for preview in 1.4.30:
Detailed descriptions of these features are available in this blog post and the corresponding pages of Kotlin documentation.
JVM records support
Java is evolving fast, and to make sure Kotlin remains interoperable with it, we've introduced support for one of its latest features – record classes.
Kotlin's support for JVM records includes bidirectional interoperability:
In Kotlin code, you can use Java record classes like you would use typical classes with properties.
To use a Kotlin class as a record in Java code, make it a
data
class and mark it with the@JvmRecord
annotation.
Sealed interfaces
Kotlin interfaces can now have the sealed
modifier, which works on interfaces in the same way it works on classes: all implementations of a sealed interface are known at compile time.
You can rely on that fact, for example, to write exhaustive when
expressions.
Additionally, sealed interfaces enable more flexible restricted class hierarchies because a class can directly inherit more than one sealed interface.
Package-wide sealed class hierarchies
Sealed classes can now have subclasses in all files of the same compilation unit and the same package. Previously, all subclasses had to appear in the same file.
Direct subclasses may be top-level or nested inside any number of other named classes, named interfaces, or named objects.
The subclasses of a sealed class must have a name that is properly qualified – they cannot be local or anonymous objects.
Inline classes
Inline classes are a subset of value-based classes that only hold values. You can use them as wrappers for a value of a certain type without the additional overhead that comes from using memory allocations.
Inline classes can be declared with the value
modifier before the name of the class:
The JVM backend also requires a special @JvmInline
annotation:
The inline
modifier is now deprecated with a warning.
Kotlin/JVM
Kotlin/JVM has received a number of improvements, both internal and user-facing. Here are the most notable among them:
New default JVM target: 1.8
Stable JVM IR backend
The IR-based backend for the Kotlin/JVM compiler is now Stable and enabled by default.
Starting from Kotlin 1.4.0, early versions of the IR-based backend were available for preview, and it has now become the default for language version 1.5
. The old backend is still used by default for earlier language versions.
You can find more details about the benefits of the IR backend and its future development in this blog post.
If you need to use the old backend in Kotlin 1.5.0, you can add the following lines to the project's configuration file:
In Gradle:
In Maven:
New default JVM target: 1.8
The default target version for Kotlin/JVM compilations is now 1.8
. The 1.6
target is deprecated.
If you need a build for JVM 1.6, you can still switch to this target. Learn how:
SAM adapters via invokedynamic
Kotlin 1.5.0 now uses dynamic invocations (invokedynamic
) for compiling SAM (Single Abstract Method) conversions:
Over any expression if the SAM type is a Java interface
Over lambda if the SAM type is a Kotlin functional interface
The new implementation uses LambdaMetafactory.metafactory()
and auxiliary wrapper classes are no longer generated during compilation. This decreases the size of the application's JAR, which improves the JVM startup performance.
To roll back to the old implementation scheme based on anonymous class generation, add the compiler option -Xsam-conversions=class
.
Learn how to add compiler options in Gradle, Maven, and the command-line compiler.
Lambdas via invokedynamic
Kotlin 1.5.0 is introducing experimental support for compiling plain Kotlin lambdas (which are not converted to an instance of a functional interface) into dynamic invocations (invokedynamic
). The implementation produces lighter binaries by using LambdaMetafactory.metafactory()
, which effectively generates the necessary classes at runtime. Currently, it has three limitations compared to ordinary lambda compilation:
A lambda compiled into invokedynamic is not serializable.
Calling
toString()
on such a lambda produces a less readable string representation.Experimental
reflect
API does not support lambdas created withLambdaMetafactory
.
To try this feature, add the -Xlambdas=indy
compiler option. We would be grateful if you could share your feedback on it using this YouTrack ticket.
Learn how to add compiler options in Gradle, Maven, and command-line compiler.
Deprecation of @JvmDefault and old Xjvm-default modes
Prior to Kotlin 1.4.0, there was the @JvmDefault
annotation along with -Xjvm-default=enable
and -Xjvm-default=compatibility
modes. They served to create the JVM default method for any particular non-abstract member in the Kotlin interface.
In Kotlin 1.4.0, we introduced the new Xjvm-default
modes, which switch on default method generation for the whole project.
In Kotlin 1.5.0, we are deprecating @JvmDefault
and the old Xjvm-default modes: -Xjvm-default=enable
and -Xjvm-default=compatibility
.
Improvements to handling nullability annotations
Kotlin supports handling type nullability information from Java with nullability annotations. Kotlin 1.5.0 introduces a number of improvements for the feature:
It reads nullability annotations on type arguments in compiled Java libraries that are used as dependencies.
It supports nullability annotations with the
TYPE_USE
target for:Arrays
Varargs
Fields
Type parameters and their bounds
Type arguments of base classes and interfaces
If a nullability annotation has multiple targets applicable to a type, and one of these targets is
TYPE_USE
, thenTYPE_USE
is preferred. For example, the method signature@Nullable String[] f()
becomesfun f(): Array<String?>!
if@Nullable
supports bothTYPE_USE
andMETHOD
as targets.
For these newly supported cases, using the wrong type nullability when calling Java from Kotlin produces warnings. Use the -Xtype-enhancement-improvements-strict-mode
compiler option to enable strict mode for these cases (with error reporting).
Kotlin/Native
Kotlin/Native is now more performant and stable. The notable changes are:
Performance improvements
In 1.5.0, Kotlin/Native is receiving a set of performance improvements that speed up both compilation and execution.
Compiler caches are now supported in debug mode for linuxX64
(only on Linux hosts) and iosArm64
targets. With compiler caches enabled, most debug compilations complete much faster, except for the first one. Measurements showed about a 200% speed increase on our test projects.
To use compiler caches for new targets, opt in by adding the following lines to the project's gradle.properties
:
For
linuxX64
:kotlin.native.cacheKind.linuxX64=static
For
iosArm64
:kotlin.native.cacheKind.iosArm64=static
If you encounter any issues after enabling the compiler caches, please report them to our issue tracker YouTrack.
Other improvements speed up the execution of Kotlin/Native code:
Trivial property accessors are inlined.
trimIndent()
on string literals is evaluated during the compilation.
Deactivation of the memory leak checker
The built-in Kotlin/Native memory leak checker has been disabled by default.
It was initially designed for internal use, and it is able to find leaks only in a limited number of cases, not all of them. Moreover, it later turned out to have issues that can cause application crashes. So we've decided to turn off the memory leak checker.
The memory leak checker can still be useful for certain cases, for example, unit testing. For these cases, you can enable it by adding the following line of code:
Note that enabling the checker for the application runtime is not recommended.
Kotlin/JS
Kotlin/JS is receiving evolutionary changes in 1.5.0. We're continuing our work on moving the JS IR compiler backend towards stable and shipping other updates:
Upgrade to webpack 5
The Kotlin/JS Gradle plugin now uses webpack 5 for browser targets instead of webpack 4. This is a major webpack upgrade that brings incompatible changes. If you're using a custom webpack configuration, be sure to check the webpack 5 release notes.
Frameworks and libraries for the IR compiler
Along with working on the IR-based backend for Kotlin/JS compiler, we encourage and help library authors to build their projects in both
mode. This means they are able to produce artifacts for both Kotlin/JS compilers, therefore growing the ecosystem for the new compiler.
Many well-known frameworks and libraries are already available for the IR backend: KVision, fritz2, doodle, and others. If you're using them in your project, you can already build it with the IR backend and see the benefits it brings.
If you're writing your own library, compile it in the 'both' mode so that your clients can also use it with the new compiler.
Kotlin Multiplatform
In Kotlin 1.5.0, choosing a testing dependency for each platform has been simplified and it is now done automatically by the Gradle plugin.
A new API for getting a char category is now available in multiplatform projects.
Standard library
The standard library has received a range of changes and improvements, from stabilizing experimental parts to adding new features:
Stable locale-agnostic API for uppercase/lowercase text
New API for getting a char category now available in multiplatform code
You can learn more about the standard library changes in this blog post.
Stable unsigned integer types
The UInt
, ULong
, UByte
, UShort
unsigned integer types are now Stable. The same goes for operations on these types, ranges, and progressions of them. Unsigned arrays and operations on them remain in Beta.
Stable locale-agnostic API for upper/lowercasing text
This release brings a new locale-agnostic API for uppercase/lowercase text conversion. It provides an alternative to the toLowerCase()
, toUpperCase()
, capitalize()
, and decapitalize()
API functions, which are locale-sensitive. The new API helps you avoid errors due to different locale settings.
Kotlin 1.5.0 provides the following fully Stable alternatives:
For
String
functions:Earlier versions
1.5.0 alternative
String.toUpperCase()
String.uppercase()
String.toLowerCase()
String.lowercase()
String.capitalize()
String.replaceFirstChar { it.uppercase() }
String.decapitalize()
String.replaceFirstChar { it.lowercase() }
For
Char
functions:Earlier versions
1.5.0 alternative
Char.toUpperCase()
Char.uppercaseChar(): Char
Char.uppercase(): String
Char.toLowerCase()
Char.lowercaseChar(): Char
Char.lowercase(): String
Char.toTitleCase()
Char.titlecaseChar(): Char
Char.titlecase(): String
The old API functions are marked as deprecated and will be removed in a future release.
See the full list of changes to the text processing functions in KEEP.
Stable char-to-integer conversion API
Starting from Kotlin 1.5.0, new char-to-code and char-to-digit conversion functions are Stable. These functions replace the current API functions, which were often confused with the similar string-to-Int conversion.
The new API removes this naming confusion, making the code behavior more transparent and unambiguous.
This release introduces Char
conversions that are divided into the following sets of clearly named functions:
Functions to get the integer code of
Char
and to constructChar
from the given code:
Functions to convert
Char
to the numeric value of the digit it represents:
An extension function for
Int
to convert the non-negative single digit it represents to the correspondingChar
representation:
The old conversion APIs, including Number.toChar()
with its implementations (all except Int.toChar()
) and Char
extensions for conversion to a numeric type, like Char.toInt()
, are now deprecated.
Learn more about the char-to-integer conversion API in KEEP.
Stable Path API
The experimental Path API with extensions for java.nio.file.Path
is now Stable.
Floored division and the mod operator
New operations for modular arithmetics have been added to the standard library:
floorDiv()
returns the result of floored division. It is available for integer types.mod()
returns the remainder of floored division (modulus). It is available for all numeric types.
These operations look quite similar to the existing division of integers and rem() function (or the %
operator), but they work differently on negative numbers:
a.floorDiv(b)
differs from a regular/
in thatfloorDiv
rounds the result down (towards the lesser integer), whereas/
truncates the result to the integer closer to 0.a.mod(b)
is the difference betweena
anda.floorDiv(b) * b
. It's either zero or has the same sign asb
, whilea % b
can have a different one.
Duration API changes
There is an experimental Duration class for representing duration amounts in different time units. In 1.5.0, the Duration API has received the following changes:
Internal value representation now uses
Long
instead ofDouble
to provide better precision.There is a new API for conversion to a particular time unit in
Long
. It comes to replace the old API, which operates withDouble
values and is now deprecated. For example,Duration.inWholeMinutes
returns the value of the duration expressed asLong
and replacesDuration.inMinutes
.There are new companion functions for constructing a
Duration
from a number. For example,Duration.seconds(Int)
creates aDuration
object representing an integer number of seconds. Old extension properties likeInt.seconds
are now deprecated.
New API for getting a char category now available in multiplatform code
Kotlin 1.5.0 introduces the new API for getting a character's category according to Unicode in multiplatform projects. Several functions are now available in all the platforms and in the common code.
Functions for checking whether a char is a letter or a digit:
Functions for checking the case of a char:
Some other functions:
The property Char.category
and its return type enum class CharCategory
, which indicates a char's general category according to Unicode, are now also available in multiplatform projects.
New collections function firstNotNullOf()
The new firstNotNullOf()
and firstNotNullOfOrNull()
functions combine mapNotNull()
with first()
or firstOrNull()
. They map the original collection with the custom selector function and return the first non-null value. If there is no such value, firstNotNullOf()
throws an exception, and firstNotNullOfOrNull()
returns null.
Strict version of String?.toBoolean()
Two new functions introduce case-sensitive strict versions of the existing String?.toBoolean():
String.toBooleanStrict()
throws an exception for all inputs except the literalstrue
andfalse
.String.toBooleanStrictOrNull()
returns null for all inputs except the literalstrue
andfalse
.
kotlin-test library
The kotlin-test library introduces some new features:
Simplified test dependencies usage in multiplatform projects
Automatic selection of a testing framework for Kotlin/JVM source sets
Simplified test dependencies usage in multiplatform projects
Now you can use the kotlin-test
dependency to add dependencies for testing in the commonTest
source set, and the Gradle plugin will infer the corresponding platform dependencies for each test source set:
kotlin-test-junit
for JVM source sets, see automatic choice of a testing framework for Kotlin/JVM source setskotlin-test-js
for Kotlin/JS source setskotlin-test-common
andkotlin-test-annotations-common
for common source setsNo extra artifact for Kotlin/Native source sets
Additionally, you can use the kotlin-test
dependency in any shared or platform-specific source set.
An existing kotlin-test setup with explicit dependencies will continue to work both in Gradle and in Maven.
Learn more about setting dependencies on test libraries.
Automatic selection of a testing framework for Kotlin/JVM source sets
The Gradle plugin now chooses and adds a dependency on a testing framework automatically. All you need to do is add the dependency kotlin-test
in the common source set.
Gradle uses JUnit 4 by default. Therefore, the kotlin("test")
dependency resolves to the variant for JUnit 4, namely kotlin-test-junit
:
You can choose JUnit 5 or TestNG by calling useJUnitPlatform()
or useTestNG()
in the test task:
You can disable automatic testing framework selection by adding the line kotlin.test.infer.jvm.variant=false
to the project's gradle.properties
.
Learn more about setting dependencies on test libraries.
Assertion function updates
This release brings new assertion functions and improves the existing ones.
The kotlin-test
library now has the following features:
Checking the type of a value
You can use the new
assertIs<T>
andassertIsNot<T>
to check the type of a value:@Test fun testFunction() { val s: Any = "test" assertIs<String>(s) // throws AssertionError mentioning the actual type of s if the assertion fails // can now print s.length because of contract in assertIs println("${s.length}") }Because of type erasure, this assert function only checks whether the
value
is of theList
type in the following example and doesn't check whether it's a list of the particularString
element type:assertIs<List<String>>(value)
.Comparing the container content for arrays, sequences, and arbitrary iterables
There is a new set of overloaded
assertContentEquals()
functions for comparing content for different collections that don't implement structural equality:@Test fun test() { val expectedArray = arrayOf(1, 2, 3) val actualArray = Array(3) { it + 1 } assertContentEquals(expectedArray, actualArray) }New overloads to
assertEquals()
andassertNotEquals()
forDouble
andFloat
numbersThere are new overloads for the
assertEquals()
function that make it possible to compare twoDouble
orFloat
numbers with absolute precision. The precision value is specified as the third parameter of the function:@Test fun test() { val x = sin(PI) // precision parameter val tolerance = 0.000001 assertEquals(0.0, x, tolerance) }New functions for checking the content of collections and elements
You can now check whether the collection or element contains something with the
assertContains()
function. You can use it with Kotlin collections and elements that have thecontains()
operator, such asIntRange
,String
, and others:@Test fun test() { val sampleList = listOf<String>("sample", "sample2") val sampleString = "sample" assertContains(sampleList, sampleString) // element in collection assertContains(sampleString, "amp") // substring in string }assertTrue()
,assertFalse()
,expect()
functions are now inlineFrom now on, you can use these as inline functions, so it's possible to call suspend functions inside a lambda expression:
@Test fun test() = runBlocking<Unit> { val deferred = async { "Kotlin is nice" } assertTrue("Kotlin substring should be present") { deferred.await() .contains("Kotlin") } }
kotlinx libraries
Along with Kotlin 1.5.0, we are releasing new versions of the kotlinx libraries:
kotlinx.coroutines
1.5.0-RCkotlinx.serialization
1.2.1kotlinx-datetime
0.2.0
Coroutines 1.5.0-RC
kotlinx.coroutines
1.5.0-RC is here with:
Stable reactive integrations
And more
Starting with Kotlin 1.5.0, experimental coroutines are disabled and the -Xcoroutines=experimental
flag is no longer supported.
Learn more in the changelog and the kotlinx.coroutines
1.5.0 release blog post.
Serialization 1.2.1
kotlinx.serialization
1.2.1 is here with:
Improvements to JSON serialization performance
Support for multiple names in JSON serialization
Experimental .proto schema generation from
@Serializable
classesAnd more
Learn more in the changelog and the kotlinx.serialization
1.2.1 release blog post.
dateTime 0.2.0
kotlinx-datetime
0.2.0 is here with:
@Serializable
Datetime objectsNormalized API of
DateTimePeriod
andDatePeriod
And more
Learn more in the changelog and the kotlinx-datetime
0.2.0 release blog post.
Migrating to Kotlin 1.5.0
IntelliJ IDEA and Android Studio will suggest updating the Kotlin plugin to 1.5.0 once it is available.
To migrate existing projects to Kotlin 1.5.0, just change the Kotlin version to 1.5.0
and re-import your Gradle or Maven project. Learn how to update to Kotlin 1.5.0.
To start a new project with Kotlin 1.5.0, update the Kotlin plugin and run the Project Wizard from File | New | Project.
The new command-line compiler is available for downloading on the GitHub release page.
Kotlin 1.5.0 is a feature release and therefore can bring incompatible changes to the language. Find the detailed list of such changes in the Compatibility Guide for Kotlin 1.5.