Kotlin Reference
Kotlin Reference
Table of Contents
Overview 8
Kotlin/JS Overview 11
Multiplatform programming 19
What's New 21
Standard library 73
Tooling 75
Kotlin Releases 97
Idioms 114
Basics 136
2
Packages 146
Properties 161
Interfaces 165
Extensions 171
Generics 181
Delegation 199
Functions 208
Collections 225
Iterators 233
Sequences 238
3
Collection Operations Overview 241
Filtering 248
Grouping 252
Coroutines 277
Channels 337
4
Share code on platforms 377
Equality 427
Exceptions 436
Annotations 438
Reflection 442
Serialization 447
Reference 469
Grammar 474
5
Java Interop 495
JavaScript 516
Native 545
Tools 600
6
Kotlin Compiler Options 619
Evolution 641
FAQ 675
FAQ 675
7
Overview
Using Kotlin for Server-side Development
Kotlin is a great t for developing server-side applications, allowing you to write concise and expressive
code while maintaining full compatibility with existing Java-based technology stacks and a smooth learning
curve:
— Expressiveness: Kotlin's innovative language features, such as its support for type-safe builders and
delegated properties, help build powerful and easy-to-use abstractions.
— Scalability: Kotlin's support for coroutines helps build server-side applications that scale to massive
numbers of clients with modest hardware requirements.
— Interoperability: Kotlin is fully compatible with all Java-based frameworks, which lets you stay on your
familiar technology stack while reaping the bene ts of a more modern language.
— Migration: Kotlin supports gradual, step by step migration of large codebases from Java to Kotlin. You
can start writing new code in Kotlin while keeping older parts of your system in Java.
— Tooling: In addition to great IDE support in general, Kotlin o ers framework-speci c tooling (for
example, for Spring) in the plugin for IntelliJ IDEA Ultimate.
— Learning Curve: For a Java developer, getting started with Kotlin is very easy. The automated Java to
Kotlin converter included in the Kotlin plugin helps with the rst steps. Kotlin Koans o er a guide
through the key features of the language with a series of interactive exercises.
— Vert.x, a framework for building reactive Web applications on the JVM, o ers dedicated support for
Kotlin, including full documentation.
— Ktor is a framework built by JetBrains for creating Web applications in Kotlin, making use of coroutines
for high scalability and o ering an easy-to-use and idiomatic API.
— kotlinx.html is a DSL that can be used to build HTML in a Web application. It serves as an alternative to
traditional templating systems such as JSP and FreeMarker.
— Micronaut is a modern, JVM-based, full-stack framework for building modular, easily testable
microservice and serverless applications. It comes with a lot of built-in, handy features.
— http4k is the functional toolkit with a tiny footprint for Kotlin HTTP applications, written in pure Kotlin.
The library is based on the "Your Server as a Function" paper from Twitter and represents modeling both
HTTP Servers and Clients as simple Kotlin functions that can be composed together.
— Javalin is a very lightweight web framework for Kotlin and Java which supports WebSockets, HTTP2 and
async requests.
8
— The available options for persistence include direct JDBC access, JPA, as well as using NoSQL databases
through their Java drivers. For JPA, the kotlin-jpa compiler plugin adapts Kotlin-compiled classes to the
requirements of the framework.
To deploy Kotlin applications on Heroku, you can follow the o cial Heroku tutorial.
AWS Labs provides a sample project showing the use of Kotlin for writing AWS Lambda functions.
Google Cloud Platform o ers a series of tutorials for deploying Kotlin applications to GCP, both for Ktor and
App Engine and Spring and App engine. In addition there is an interactive code lab for deploying a Kotlin
Spring application.
JetBrains Account, the system responsible for the entire license sales and validation process at JetBrains, is
written in 100% Kotlin and has been running in production since 2015 with no major issues.
Next Steps
— The Creating Web Applications with Http Servlets and Creating a RESTful Web Service with Spring Boot
tutorials show you how you can build and run very small Web applications in Kotlin.
— For a more in-depth introduction to the language, check out the reference documentation on this site
and Kotlin Koans.
— Micronaut also has a lot of well-detailed guides, showing how you can build microservices in Kotlin.
— http4k provides the CLI to generate fully formed projects, and a starter repo to generate an entire CD
pipeline using GitHub, Travis, and Heroku with a single bash command.
9
Using Kotlin for Android Development
Android mobile development has been Kotlin- rst since Google I/O in 2019.
— Less code combined with greater readability. Spend less time writing your code and working to
understand the code of others.
— Mature language and environment. Since its creation in 2011, Kotlin has developed continuously, not
only as a language but as a whole ecosystem with robust tooling. Now it's seamlessly integrated in
Android Studio and is actively used by many companies for developing Android applications.
— Kotlin support in Android Jetpack and other libraries. KTX extensions add Kotlin language features,
such as coroutines, extension functions, lambdas, and named parameters, to existing Android libraries.
— Interoperability with Java. You can use Kotlin along with the Java programming language in your
applications without needing to migrate all your code to Kotlin.
— Support for multiplatform development. You can use Kotlin for developing not only Android but also
iOS, backend, and web applications. Enjoy the bene ts of sharing the common code among the
platforms.
— Code safety. Less code and better readability lead to fewer errors. The Kotlin compiler detects these
remaining errors, making the code safe.
— Easy learning. Kotlin is very easy to learn, especially for Java developers.
— Big community. Kotlin has great support and many contributions from the community, which is growing
all over the world. According to Google, over 60% of the top 1000 apps on the Play Store use Kotlin.
Many startups and Fortune 500 companies have already developed Android applications using Kotlin – see
the list at the Google website for Kotlin developers.
If you want to start using Kotlin for Android development, read Google’s recommendation for getting
started with Kotlin on Android.
If you're new to Android and want to learn to create applications with Kotlin, check out this Udacity course.
10
Kotlin/JS Overview
Kotlin/JS provides the ability to transpile your Kotlin code, the Kotlin standard library, and any compatible
dependencies to JavaScript. The current implementation of Kotlin/JS targets ES5.
The recommended way to use Kotlin/JS is via the kotlin.js and kotlin.multiplatform Gradle
plugins. They provide a central and convenient way to set up and control Kotlin projects targeting JavaScript.
This includes essential functionality such as controlling the bundling of your application, adding JavaScript
dependencies directly from npm, and more. To get an overview of the available options, check out the
Kotlin/JS project setup documentation.
— Use Kotlin's multiplatform projects to share code with other Kotlin targets
— All Kotlin/JS functionality can also be accessed when using the Kotlin multiplatform Gradle plugin.
— If you have a backend written in Kotlin, you can share common code such as data models or
validation logic with a frontend written in Kotlin/JS, allowing you to write and maintain full-stack
web applications.
— You could also share business logic between your web interface and mobile apps for Android and
iOS, and avoid duplicating commonly used functionality like providing abstractions around REST API
endpoints, user authentication, or your domain models.
11
from your Kotlin code that can be consumed as modules from any code base written in JavaScript or
TypeScript, regardless of other frameworks or technologies used. This approach of creating hybrid
applications allows you to leverage the competencies that you and your team might already have
around web development, while helping you reduce the amount of duplicated work, and making it
easier to keep your web target consistent with other targets of your application.
Of course, this is not a complete list of how you can use Kotlin/JS to your advantage, but merely a selection
of cherry-picked cases. We invite you to experiment with combinations of these use cases, and nd out what
works best for your project.
Regardless of your speci c use case, Kotlin/JS projects can use compatible libraries from the Kotlin
ecosystem, as well as third-party libraries from the JavaScript and TypeScript ecosystems. To use the
latter from Kotlin code, you can either provide your own typesafe wrappers, use community-maintained
wrappers, or let Dukat automatically generate Kotlin declarations for you. Using the Kotlin/JS-exclusive
dynamic type allows you to loosen the constraints of Kotlin's type system, allowing you to skip creating
detailed library wrappers - at the expense of type safety.
Kotlin/JS is also compatible with the most common module systems: UMD, CommonJS, and AMD. Being able
to produce and consume modules means that you can interact with the JavaScript ecosystem in a structured
manner.
In this video, Kotlin Developer Advocate Sebastian Aigner will explain the main Kotlin/JS bene ts to you,
share some tips and use cases, and also tell you about the plans and upcoming features for Kotlin/JS.
To start using Kotlin for JavaScript, please refer to the Setting up a Kotlin/JS project, or pick a hands-on lab
from the next section to work through.
12
They include sample projects, which can serve as jumping-o points for your own projects, and contain
useful snippets and patterns.
— Building Web Applications with React and Kotlin/JS guides you through the process of building a simple
web application using the React framework, shows how a typesafe Kotlin DSL for HTML makes it
convenient to build reactive DOM elements, and illustrates how to use third-party React components,
and how to obtain information from APIs, while writing the whole application logic in pure Kotlin/JS.
— Building a Full Stack Web App with Kotlin Multiplatform teaches the concepts behind building an
application that targets Kotlin/JVM and Kotlin/JS by building a client-server application that makes use of
common code, serialization, and other multiplatform paradigms. It also provides a brief introduction into
working with Ktor both as a server- and client-side framework.
To learn more about the available features in the new Kotlin/JS IR compiler and how to try it for your
project, visit the documentation.
13
Kotlin/Native for Native
Kotlin/Native is a technology for compiling Kotlin code to native binaries, which can run without a virtual
machine. It is an LLVM based backend for the Kotlin compiler and native implementation of the Kotlin
standard library.
Why Kotlin/Native?
Kotlin/Native is primarily designed to allow compilation for platforms where virtual machines are not
desirable or possible, for example, embedded devices or iOS. It solves the situations when a developer
needs to produce a self-contained program that does not require an additional runtime or virtual machine.
Target Platforms
Kotlin/Native supports the following platforms:
Interoperability
Kotlin/Native supports two-way interoperability with the Native world. On the one hand, the compiler
creates:
On the other hand, Kotlin/Native supports interoperability to use existing libraries directly from
Kotlin/Native:
It is easy to include a compiled Kotlin code into existing projects written in C, C++, Swift, Objective-C, and
other languages. It is also easy to use existing native code, static or dynamic C libraries, Swift/Objective-C
frameworks, graphical engines, and anything else directly from Kotlin/Native.
Kotlin/Native libraries help to share Kotlin code between projects. POSIX, gzip, OpenGL, Metal, Foundation,
and many other popular libraries and Apple frameworks are pre-imported and included as Kotlin/Native
libraries into the compiler package.
14
Multiplatform projects allow sharing common Kotlin code between multiple platforms, including Android,
iOS, JVM, JavaScript, and native. Multiplatform libraries provide required APIs for the common Kotlin code
and help develop shared parts of a project in Kotlin code in one place and share it with all or several target
platforms.
You can use Kotlin Multiplatform Mobile (KMM) to create multiplatform mobile applications with code
shared between Android and iOS.
What's next?
New to Kotlin? Take a look at the Getting Started page.
Documentation
Tutorials
— Hello Kotlin/Native
— Types mapping between C and Kotlin/Native
— Kotlin/Native as a Dynamic Library
— Kotlin/Native as an Apple Framework
Sample projects
— Kotlin Multiplatform Mobile samples
— Kotlin/Native sources and examples
— KotlinConf app
— KotlinConf Spinner app
— Kotlin/Native sources and examples (.tgz)
— Kotlin/Native sources and examples (.zip)
15
Kotlin for Data Science
From building data pipelines to productionizing machine learning models, Kotlin can be a great choice for
working with data:
Interactive editors
Notebooks such as Jupyter Notebook and Apache Zeppelin provide convenient tools for data visualization
and exploratory research. Kotlin integrates with these tools to help you explore data, share your ndings
with colleagues, or build up your data science and machine learning skills.
The Jupyter Notebook is an open-source web application that allows you to create and share documents
(aka "notebooks") that can contain code, visualizations, and markdown text. Kotlin-jupyter is an open source
project that brings Kotlin support to Jupyter Notebook.
Check out Kotlin kernel's GitHub repo for installation instructions, documentation, and examples.
Apache Zeppelin is a popular web-based solution for interactive data analytics. It provides strong support
for the Apache Spark cluster computing system, which is particularly useful for data engineering. Starting
from version 0.9.0, Apache Zeppelin comes with bundled Kotlin interpreter.
Libraries
The ecosystem of libraries for data-related tasks created by the Kotlin community is rapidly expanding. Here
are some libraries that you may nd useful:
Kotlin libraries
— KotlinDL is a high-level Deep Learning API written in Kotlin and inspired by Keras. It o ers simple APIs
for training deep learning models from scratch, importing existing Keras models for inference, and
leveraging transfer learning for tweaking existing pre-trained models to your tasks.
— Kotlin for Apache Spark adds a missing layer of compatibility between Kotlin and Apache Spark. It allows
Kotlin developers to use familiar language features such as data classes, and lambda expressions as
simple expressions in curly braces or method references.
— kotlin-statistics is a library providing extension functions for exploratory and production statistics. It
supports basic numeric list/sequence/array functions (from sum to skewness ), slicing operators (such
as countBy , simpleRegressionBy ), binning operations, discrete PDF sampling, naive bayes classi er,
clustering, linear regression, and much more.
16
— kmath is a library inspired by NumPy. This library supports algebraic structures and operations, array-
like structures, math expressions, histograms, streaming operations, a wrapper around commons-math
and koma, and more.
— krangl is a library inspired by R's dplyr and Python's pandas. This library provides functionality for data
manipulation using a functional-style API; it also includes functions for ltering, transforming,
aggregating, and reshaping tabular data.
— lets-plot is a plotting library for statistical data written in Kotlin. Lets-Plot is multiplatform and can be
used not only with JVM, but also with JS and Python.
— kravis is another library for the visualization of tabular data inspired by R's ggplot.
Java libraries
Since Kotlin provides rst-class interop with Java, you can also use Java libraries for data science in your
Kotlin code. Here are some examples of such libraries:
— Smile - a comprehensive machine learning, natural language processing, linear algebra, graph,
interpolation, and visualization system. Besides Java API, Smile also provides a functional Kotlin API along
with Scala and Clojure API.
— Smile-NLP-kt - a Kotlin rewrite of the Scala implicits for the natural language processing part of Smile
in the format of extension functions and interfaces.
— Apache Commons Math - a general math, statistics, and machine learning library for Java
If this list doesn’t cover your needs, you can nd more options in the Kotlin Data Science Resources digest
from Thomas Nield.
17
Coroutines for asynchronous programming and more
Asynchronous or non-blocking programming is the new reality. Whether we're creating server-side, desktop
or mobile applications, it's important that we provide an experience that is not only uid from the user's
perspective, but scalable when needed.
There are many approaches to this problem, and in Kotlin we take a very exible one by providing
Coroutine support at the language level and delegating most of the functionality to libraries, much in line
with Kotlin's philosophy.
As a bonus, coroutines not only open the doors to asynchronous programming, but also provide a wealth of
other possibilities such as concurrency, actors, etc.
How to Start
— Coroutines Guide
— Basics
— Channels
— Coroutine Context and Dispatchers
— Shared Mutable State and Concurrency
— Asynchronous Flow
Recommended tutorials:
Example Projects
18
Multiplatform programming
Multiplatform projects are in Alpha. Language features and tooling may change in future Kotlin
versions.
Support for multiplatform programming is one of Kotlin’s key bene ts. It reduces time spent writing and
maintaining the same code for di erent platforms while retaining the exibility and bene ts of native
programming.
— Common Kotlin includes the language, core libraries, and basic tools. Code written in common Kotlin
works everywhere on all platforms.
— With Kotlin Multiplatform libraries, you can reuse the multiplatform logic in common and platform-
speci c code. Common code can rely on a set of libraries that cover everyday tasks such as HTTP,
serialization, and managing coroutines.
— To interop with platforms, use platform-speci c versions of Kotlin. Platform-speci c versions of Kotlin
(Kotlin/JVM, Kotlin/JS, Kotlin/Native) include extensions to the Kotlin language, and platform-speci c
libraries and tools.
— Through these platforms you can access the platform native code (JVM, JS, and Native) and leverage all
native capabilities.
With Kotlin Multiplatform, spend less time on writing and maintaining the same code for di erent platforms
– just share it using the mechanisms Kotlin provides:
— Share code among all platforms used in your project. Use it for sharing the common business logic that
applies to all platforms.
— Share code among some platforms included in your project but not all. Do this when you can reuse
much of the code in similar platforms.
If you need to access platform-speci c APIs from the shared code, use the Kotlin mechanism of expected
and actual declarations.
With this mechanism, a common source set de nes an expected declaration, and platform source sets must
provide the actual declaration that corresponds to the expected declaration. This works for most Kotlin
declarations, such as functions, classes, interfaces, enumerations, properties, and annotations.
//Common
expect fun randomUUID(): String
//Android
import java.util.*
actual fun randomUUID() = UUID.randomUUID().toString()
//iOS
import platform.Foundation.NSUUID
actual fun randomUUID(): String = NSUUID().UUIDString()
Use cases
Android — iOS
19
Sharing code between mobile platforms is one of the major Kotlin Multiplatform use cases. With Kotlin
Multiplatform Mobile (KMM), you can build multiplatform mobile applications sharing code, such as
business logic, connectivity, and more, between Android and iOS.
Client — Server
Another scenario when code sharing may bring bene ts is a connected application where the logic can be
reused on both the server and the client side running in the browser. This is covered by Kotlin
Multiplatform as well.
The Ktor framework is suitable for building asynchronous servers and clients in connected systems.
What's next?
New to Kotlin? Visit Getting started with Kotlin.
Documentation
Tutorials
— Creating a KMM application shows how to create a mobile application that works on Android and iOS
with the help of the KMM plugin for Android Studio. Create, run, and test your rst multiplatform mobile
application.
— Creating a multiplatform Kotlin library teaches how to create a multiplatform library available for JVM, JS,
and Native and which can be used from any other common code (for example, shared with Android and
iOS). It also shows how to write tests which will be executed on all platforms and use an e cient
implementation provided by a speci c platform.
— Building a full stack web app with Kotlin Multiplatform teaches the concepts behind building an
application that targets Kotlin/JVM and Kotlin/JS by building a client-server application that makes use of
shared code, serialization, and other multiplatform paradigms. It also provides a brief introduction to
working with Ktor both as a server- and client-side framework.
Sample projects
— Kotlin Multiplatform Mobile samples
— KotlinConf app
— KotlinConf Spinner app
20
What's New
What's New in Kotlin 1.4.30
Kotlin 1.4.30 o ers preview versions of new language features, promotes the new IR backend of the
Kotlin/JVM compiler to Beta, and ships various performance and functional improvements.
You can also learn about new features in this blog post.
Language features
Kotlin/JVM
Kotlin/Native
— Performance improvements
— Apple watchOS 64-bit simulator target
— Support for Xcode 12.2 libraries
Kotlin/JS
Standard library
Serialization updates
Language features
21
Kotlin 1.5.0 is going to deliver new language features – JVM records support, sealed interfaces, and Stable
inline classes. In Kotlin 1.4.30, you can try these features and improvements in preview mode. We’d be very
grateful if you share your feedback with us in the corresponding YouTrack tickets, as that will allow us to
address it before the release of 1.5.0.
To enable these language features and improvements in preview mode, you need to opt in by adding
speci c compiler options. See the sections below for details.
Learn more about the new features preview in this blog post.
The JVM records feature is Experimental. It may be dropped or changed at any time. Opt-in is
required (see the details below), and you should use it only for evaluation purposes. We would
appreciate your feedback on it in YouTrack.
The JDK 16 release includes plans to stabilize a new Java class type called record. To provide all the bene ts
of Kotlin and maintain its interoperability with Java, Kotlin is introducing experimental record class support.
You can use record classes that are declared in Java just like classes with properties in Kotlin. No additional
steps are required.
Starting with 1.4.30, you can declare the record class in Kotlin using the @JvmRecord annotation for a data
class:
@JvmRecord
data class User(val name: String, val age: Int)
To try the preview version of JVM records, add the compiler options -Xjvm-enable-preview and -
language-version 1.5 .
We’re continuing to work on JVM records support and we’d be very grateful if you would share your
feedback with us using this YouTrack ticket.
Sealed interfaces
Sealed interfaces are Experimental. They may be dropped or changed at any time. Opt-in is
required (see the details below), and you should use them only for evaluation purposes. We would
appreciate your feedback on them in YouTrack.
In Kotlin 1.4.30, we’re shipping the prototype of sealed interfaces. They complement sealed classes and make
it possible to build more exible restricted class hierarchies.
They can serve as “internal” interfaces that cannot be implemented outside the same module. You can rely
on that fact, for example, to write exhaustive when expressions.
22
sealed interface Polygon
Another use-case: with sealed interfaces, you can inherit a class from two or more sealed superclasses.
sealed interface Fillable {
fun fill()
}
sealed interface Polygon {
val vertices: List<Point>
}
To try the preview version of sealed interfaces, add the compiler option -language-version 1.5 . Once
you switch to this version, you’ll be able to use the sealed modi er on interfaces. We’d be very grateful if
you would share your feedback with us using this YouTrack ticket.
Package-wide hierarchies of sealed classes are Experimental. They may be dropped or changed at
any time. Opt-in is required (see the details below), and you should use them only for evaluation
purposes. We would appreciate your feedback on them in YouTrack.
Sealed classes can now form more exible hierarchies. They can have subclasses in all les of the same
compilation unit and the same package. Previously, all subclasses had to appear in the same le.
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 quali ed – they cannot
be local nor anonymous objects.
To try package-wide hierarchies of sealed classes, add the compiler option -language-version 1.5 .
We’d be very grateful if you would share your feedback with us using this YouTrack ticket.
Inline value classes are in Beta. They are almost stable, but migration steps may be required in the
future. We'll do our best to minimize any changes you have to make. We would appreciate your
feedback on the inline classes feature in YouTrack.
Kotlin 1.4.30 promotes inline classes to Beta and brings the following features and improvements to them:
23
— Since inline classes are value-based, you can de ne them using the value modi er. The inline and
value modi ers are now equivalent to each other. In future Kotlin versions, we’re planning to
deprecate the inline modi er.
From now on, Kotlin requires the @JvmInline annotation before a class declaration for the JVM
backend:
inline class Name(private val s: String)
— Inline classes can have init blocks. You can add code to be executed right after the class is
instantiated:
@JvmInline
value class Negative(val x: Int) {
init {
require(x < 0) { }
}
}
— Calling functions with inline classes from Java code: before Kotlin 1.4.30, you couldn't call functions that
accept inline classes from Java because of mangling. From now on, you can disable mangling manually.
To call such functions from Java code, you should add the @JvmName annotation before the function
declaration:
inline class UInt(val x: Int)
@JvmName("computeUInt")
fun compute(x: UInt) { }
— In this release, we’ve changed the mangling scheme for functions to x the incorrect behavior. These
changes led to ABI changes.
Starting with 1.4.30, the Kotlin compiler uses a new mangling scheme by default. Use the -Xuse-14-
inline-classes-mangling-scheme compiler ag to force the compiler to use the old 1.4.0 mangling
scheme and preserve binary compatibility.
Kotlin 1.4.30 promotes inline classes to Beta, and we are planning to make them Stable in future releases.
We’d be very grateful if you would share your feedback with us using this YouTrack ticket.
To try the preview version of inline classes, add the compiler option -Xinline-classes or -language-
version 1.5 .
Kotlin/JVM
24
The IR-based compiler backend for Kotlin/JVM, which was presented in 1.4.0 in Alpha, has reached Beta. This
is the last pre-stable level before the IR backend becomes the default for the Kotlin/JVM compiler.
We’re now dropping the restriction on consuming binaries produced by the IR compiler. Previously, you
could use code compiled by the new JVM IR backend only if you had enabled the new backend. Starting
from 1.4.30, there is no such limitation, so you can use the new backend to build components for third-party
use, such as libraries. Try the Beta version of the new backend and share your feedback in our issue tracker.
To enable the new JVM IR backend, add the following lines to the project’s con guration le:
— In Gradle:
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile) {
kotlinOptions.useIR = true
}
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile::class) {
kotlinOptions.useIR = true
}
— In Maven:
<configuration>
<args>
<arg>-Xuse-ir</arg>
</args>
</configuration>
Learn more about the changes that the JVM IR backend brings in this blog post.
Kotlin/Native
Performance improvements
Kotlin/Native has received a variety of performance improvements in 1.4.30, which has resulted in faster
compilation times. For example, the time required to rebuild the framework in the KMM Networking and
Data Storage sample has decreased from 9.5 seconds (in 1.4.10) to 4.5 seconds (in 1.4.30).
The x86 simulator target has been deprecated for watchOS since version 7.0. To keep up with the latest
watchOS versions, Kotlin/Native has the new target watchosX64 for running the simulator on 64-bit
architecture.
We have added support for the new libraries delivered with Xcode 12.2. You can now use them from Kotlin
code.
Kotlin/JS
25
Lazy initialization of top-level properties is Experimental. It may be dropped or changed at any
time. Opt-in is required (see the details below), and you should use it only for evaluation purposes.
We would appreciate your feedback on it in YouTrack.
The IR backend for Kotlin/JS is receiving a prototype implementation of lazy initialization for top-level
properties. This reduces the need to initialize all top-level properties when the application starts, and it
should signi cantly improve application start-up times.
We’ll keep working on the lazy initialization, and we ask you to try the current prototype and share your
thoughts and results in this YouTrack ticket or the #javascript channel in the o cial Kotlin Slack (get an
invite here).
To use the lazy initialization, add the -Xir-property-lazy-initialization compiler option when
compiling the code with the JS IR compiler.
Starting with 1.4.30, the Kotlin Gradle plugin supports the con guration cache feature. It speeds up the
build process: once you run the command, Gradle executes the con guration phase and calculates the task
graph. Gradle caches the result and reuses it for subsequent builds.
To start using this feature, you can use the Gradle command or set up the IntelliJ based IDE.
Standard library
The locale-agnostic API feature is Experimental. It may be dropped or changed at any time. Use it
only for evaluation purposes. We would appreciate your feedback on it in YouTrack.
This release introduces the experimental locale-agnostic API for changing the case of strings and characters.
The current toLowerCase() , toUpperCase() , capitalize() , decapitalize() API functions are
locale-sensitive. This means that di erent platform locale settings can a ect code behavior. For example, in
the Turkish locale, when the string “kotlin” is converted using toUpperCase , the result is "KOTLİN", not
"KOTLIN".
// current API
println("Needs to be capitalized".toUpperCase()) // NEEDS TO BE CAPITALIZED
// new API
println("Needs to be capitalized".uppercase()) // NEEDS TO BE CAPITALIZED
26
Earlier versions 1.4.30 alternative
String.toUpperCase() String.uppercase()
String.toLowerCase() String.lowercase()
String.capitalize() String.replaceFirstChar { it.uppercase() }
String.decapitalize() String.replaceFirstChar { it.lowercase() }
For Kotlin/JVM, there are also overloaded uppercase(), lowercase(), and titlecase()
functions with an explicit Locale parameter.
See the full list of changes to the text processing functions in KEEP.
The unambiguous API for the Char conversion feature is Experimental. It may be dropped or
changed at any time. Use it only for evaluation purposes. We would appreciate your feedback on it
in YouTrack.
The current Char to numbers conversion functions, which return UTF-16 codes expressed in di erent
numeric types, are often confused with the similar String-to-Int conversion, which returns the numeric value
of a string:
"4".toInt() // returns 4
'4'.toInt() // returns 52
// and there was no common function that would return the numeric value 4 for Char '4'
To avoid this confusion we've decided to separate Char conversions into two following sets of clearly
named functions:
— Functions to get the integer code of Char and to construct Char from the given code:
— An extension function for Int to convert the non-negative single digit it represents to the
corresponding Char representation:
fun Int.digitToChar(radix: Int): Char
27
Serialization updates
Along with Kotlin 1.4.30, we are releasing kotlinx.serialization 1.1.0-RC, which includes some new
features:
Starting with Kotlin 1.4.30, you can make inline classes serializable:
@Serializable
inline class Color(val rgb: Int)
The serialization framework does not box serializable inline classes when they are used in other serializable
classes.
Starting from 1.4.30, you can use standard JSON serializers of kotlinx.serialization for unsigned primitive
types: UInt , ULong , UByte , and UShort :
@Serializable
class Counter(val counted: UByte, val description: String)
fun main() {
val counted = 239.toUByte()
println(Json.encodeToString(Counter(counted, "tries")))
}
28
Kotlin plugin releases
The IntelliJ Kotlin plugin and IntelliJ IDEA are now on the same release cycle. To speed up the testing and
delivery of new features, the plugin and the platform have been moved to the same codebase and ship
simultaneously. Kotlin releases happen independently according to the new release cadence.
This also a ects the versioning of the Kotlin plugin. Releases now have the same version as the
simultaneous IntelliJ IDEA release. You can learn more about new release cadence in this blog post.
Cross-language conversion is possible starting with version 2020.3 of the Kotlin plugin. You can now use the
inline refactoring actions for Kotlin elements de ned in Java.
The Kotlin plugin can inline code from libraries with attached sources. That means you can inline refactor all
the Kotlin scope functions: also, let, run, apply, and with.
Also, this release brings refactoring improvements for lambda expressions. Now the IDE analyzes their
syntax more thoroughly and formats them correctly.
Structural search and replace (SSR) actions are now available for Kotlin. The SSR feature allows you to nd
and replace code patterns, taking the syntax and semantics of the source code into account.
To start using the feature, open the Structural Search dialog for your .kt le and select one of the
templates or create your own.
EditorCon g support
From 2020.3, the Kotlin API has the full support of .editorcon g les for managing code style on a directory
level.
The new experimental Jetpack Compose for Desktop templates are now available in the Kotlin Project
Wizard. You can create a project using the following templates:
—
29
— Desktop – a Compose project targeting the desktop JVM platform: Windows, Linux, or macOS.
— Multiplatform – a Multiplatform Compose project targeting the desktop JVM platform (Windows, Linux,
macOS) and Android with shared code in common modules.
To create a project, select one of the templates while creating a new project and specify the Gradle build
system. The Kotlin plugin creates all the con guration les automatically. You can try out this experimental
feature by working through Getting Started with Compose for Desktop tutorial.
Read more about Jetpack Compose features in this blog post and look through the examples of Compose
applications.
30
What's New in Kotlin 1.4.20
Kotlin 1.4.20 o ers a number of new experimental features and provides xes and improvements for
existing features, including those added in 1.4.0.
You can also learn about new features with more examples in this blog post.
Kotlin/JVM
— Java 15 target
— invokedynamic string concatenation
Kotlin/JS
Kotlin/Native
— Escape analysis
— Performance improvements and bug xes
— Opt-in wrapping of Objective-C exceptions
— CocoaPods plugin improvements
— Support for Xcode 12 libraries
Kotlin Multiplatform
Standard library
Kotlin/JVM
Improvements of Kotlin/JVM are intended to keep it up with the features of modern Java versions:
— Java 15 target
— invokedynamic string concatenation
Java 15 target
31
invokedynamic string concatenation is Experimental. It may be dropped or changed at any time.
Opt-in is required (see details below). Use it only for evaluation purposes. We appreciate your
feedback on it in YouTrack.
Kotlin 1.4.20 can compile string concatenations into dynamic invocations on JVM 9+ targets, therefore
improving the performance.
— string templates except for ones with a single non-constant argument (see KT-42457).
To enable invokedynamic string concatenation, add the -Xstring-concat compiler option with one of
the following values:
Back to top
Kotlin/JS
Kotlin/JS keeps evolving fast, and in 1.4.20 you can nd a number experimental features and improvements:
The Gradle DSL for Kotlin/JS receives a number of updates which simplify project setup and customization.
This includes webpack con guration adjustments, modi cations to the auto-generated package.json le,
and improved control over transitive dependencies.
A new con guration block commonWebpackConfig is available for the browser target. Inside it, you can
adjust common settings from a single point, instead of having to duplicate con gurations for the
webpackTask , runTask , and testTask .
To enable CSS support by default for all three tasks, add the following snippet in the
build.gradle(.kts) of your project:
browser {
commonWebpackConfig {
cssSupport.enabled = true
}
binaries.executable()
}
32
Learn more about con guring webpack bundling.
For more control over your Kotlin/JS package management and distribution, you can now add properties to
the project le package.json via the Gradle DSL.
To add custom elds to your package.json , use the customField function in the compilation's
packageJson block:
kotlin {
js(BOTH) {
compilations["main"].packageJson {
customField("hello", mapOf("one" to 1, "two" to 2))
}
}
}
Support for selective yarn dependency resolutions is Experimental. It may be dropped or changed
at any time. Use it only for evaluation purposes. We appreciate your feedback on it in YouTrack.
Kotlin 1.4.20 provides a way of con guring Yarn's selective dependency resolutions - the mechanism for
overriding dependencies of the packages you depend on.
You can use it through the YarnRootExtension inside the YarnPlugin in Gradle. To a ect the resolved
version of a package for your project, use the resolution function passing in the package name selector
(as speci ed by Yarn) and the version to which it should resolve.
rootProject.plugins.withType<YarnPlugin> {
rootProject.the<YarnRootExtension>().apply {
resolution("react", "16.0.0")
resolution("processor/decamelize", "3.0.0")
}
}
Here, all of your npm dependencies which require react will receive version 16.0.0 , and processor
will receive its dependency decamelize as version 3.0.0 .
Disabling granular workspaces is Experimental. It may be dropped or changed at any time. Use it
only for evaluation purposes. We appreciate your feedback on it in YouTrack.
To speed up build times, the Kotlin/JS Gradle plugin only installs the dependencies which are required for a
particular Gradle task. For example, the webpack-dev-server package is only installed when you execute
one of the *Run tasks, and not when you execute the assemble task. Such behavior can potentially bring
problems when you run multiple Gradle processes in parallel. When the dependency requirements clash,
the two installations of npm packages can cause errors.
33
To resolve this issue, Kotlin 1.4.20 includes an option to disable these so-called granular workspaces. This
feature is currently available through the YarnRootExtension inside the YarnPlugin in Gradle. To use
it, add the following snippet to your build.gradle.kts le:
rootProject.plugins.withType<YarnPlugin> {
rootProject.the<YarnRootExtension>().disableGranularWorkspaces()
}
To give you more convenient ways to customize your project during creation, the project wizard for Kotlin
comes with new templates for Kotlin/JS applications:
— Browser Application - a minimal Kotlin/JS Gradle project that runs in the browser.
— React Application - a React app that uses the appropriate kotlin-wrappers . It provides options to
enable integrations for style-sheets, navigational components, or state containers.
— Node.js Application - a minimal project for running in a Node.js runtime. It comes with the option to
directly include the experimental kotlinx-nodejs package.
Ignore compilation errors mode is Experimental. It may be dropped or changed at any time. Opt-in is
required (see details below). Use it only for evaluation purposes. We appreciate your feedback on it
in YouTrack.
The IR compiler for Kotlin/JS comes with a new experimental mode - compilation with errors. In this mode,
you can run you code even if it contains errors, for example, if you want to try certain things it when the
whole application is not ready yet.
— SEMANTIC : the compiler will accept code which is syntactically correct, but doesn't make sense
semantically, such as val x: String = 3 .
— SYNTAX : the compiler will accept any code, even if it contains syntax errors.
To allow compilation with errors, add the -Xerror-tolerance-policy= compiler option with one of the
values listed above.
Back to top
Kotlin/Native
Kotlin/Native's priorities in 1.4.20 are performance and polishing existing features. These are the notable
improvements:
— Escape analysis
— Performance improvements and bug xes
34
— Opt-in wrapping of Objective-C exceptions
— CocoaPods plugin improvements
— Support for Xcode 12 libraries
Escape analysis
The escape analysis mechanism is Experimental. It may be dropped or changed at any time. Use it
only for evaluation purposes. We appreciate your feedback on it in YouTrack.
Kotlin/Native receives a prototype of the new escape analysis mechanism. It improves the runtime
performance by allocating certain objects on the stack instead of the heap. This mechanism shows a 10%
average performance increase on our benchmarks, and we continue improving it so that it speeds up the
program even more.
The escape analysis runs in a separate compilation phase for the release builds (with the -opt compiler
option).
If you want to disable the escape analysis phase, use the -Xdisable-phases=EscapeAnalysis compiler
option.
Kotlin/Native receives performance improvements and bug xes in various components, including the ones
added in 1.4.0, for example, the code sharing mechanism.
Kotlin/Native now can handle exceptions thrown from Objective-C code in runtime to avoid program
crashes.
You can opt in to wrap NSException ’s into Kotlin exceptions of type ForeignException . They hold the
references to the original NSException 's. This lets you get the information about the root cause and
handle it properly.
The default behavior remains unchanged: the program terminates when an exception is thrown from the
Objective-C code.
35
CocoaPods plugin improvements
Kotlin 1.4.20 continues the set of improvements in CocoaPods integration. Namely, you can try the following
new features:
CocoaPods plugin gets an improved task execution ow. For example, if you add a new CocoaPods
dependency, existing dependencies are not rebuilt. Adding an extra target also doesn't a ect rebuilding
dependencies for existing ones.
Extended DSL
The DSL of adding CocoaPods dependencies to your Kotlin project receives new capabilites.
In addition to local Pods and Pods from the CocoaPods repository, you can add dependencies on the
following types of libraries:
Learn more about adding CocoaPods dependencies in Kotlin projects. Find examples in the Kotlin with
CocoaPods sample.
— If your Kotlin Pod has any Git, HTTP, or specRepo pod dependency, you should also specify it in the
Pod le.
— When you add a library from the custom spec, you also should specify the location of specs at the
beginning of your Pod le.
Now integration errors have a detailed description in IDEA. So if you have problems with your Pod le, you
will immediately know how to x them.
We have added support for new libraries delivered with Xcode 12. Now you can use them from the Kotlin
code.
Back to top
Kotlin Multiplatform
36
Updated structure of multiplatform library publications
Starting from Kotlin 1.4.20, there is no longer a separate metadata publication. Metadata artifacts are now
included in the root publication which stands for the whole library and is automatically resolved to the
appropriate platform-speci c artifacts when added as a dependency to the common source set.
This change of structure breaks the compatibility between projects with hierarchical project structure. If a
multiplatform project and a library it depends on both have the hierarchical project structure, then you
need to update them to Kotlin 1.4.20 or higher simultaneously. Libraries published with Kotlin 1.4.20 are
not available for using from project published with earlier versions.
Projects and libraries without the hierarchical project structure remain compatible.
Back to top
Standard library
The standard library of Kotlin 1.4.20 o ers new extensions for working with les and a better performance.
Extensions for java.nio.file.Path are Experimental. They may be dropped or changed at any
time. Opt-in is required (see details below). Use them only for evaluation purposes. We appreciate
your feedback on them in YouTrack.
Now the standard library provides experimental extensions for java.nio.file.Path . Working with the
modern JVM le API in an idiomatic Kotlin way is now similar to working with java.io.File extensions
from the kotlin.io package.
// construct path with the div (/) operator
val baseDir = Path("/base")
val subDir = baseDir / "subdirectory"
The extensions are available in the kotlin.io.path package in the kotlin-stdlib-jdk7 module. To
use the extensions, opt-in to the experimental annotation @ExperimentalPathApi .
The new implementation of String.replace() speeds up the function execution. The case-sensitive
variant uses a manual replacement loop based on indexOf , while the case-insensitive one uses regular
expression matching.
Back to top
37
Kotlin Android Extensions
In 1.4.20 the Kotlin Android Extensions plugin becomes deprecated and Parcelable implementation
generator moves to a separate plugin.
Synthetic views were presented in the Kotlin Android Extensions plugin a while ago to simplify the interaction
with UI elements and reduce boilerplate. Now Google o ers a native mechanism that does the same -
Android Jetpack's view bindings, and we're deprecating synthetic views in favor of those.
We extract the Parcelable implementations generator from kotlin-android-extensions and start the
deprecation cycle for the rest of it - synthetic views. For now, they will keep working with a deprecation
warning. In the future, you’ll need to switch your project to another solution. Here are the guidelines that
will help you migrate your Android project from synthetics to view bindings.
The Parcelable implementation generator is now available in the new kotlin-parcelize plugin.
Apply this plugin instead of kotlin-android-extensions .
38
What's New in Kotlin 1.4.0
In Kotlin 1.4.0, we ship a number of improvements in all of its components, with the focus on quality and
performance. Below you will nd the list of the most important changes in Kotlin 1.4.0.
New compiler
Kotlin/JVM
Kotlin/JS
Kotlin/Native
— Performance improvements
— Simpli ed management of CocoaPods dependencies
Kotlin Multiplatform
39
Gradle project improvements
Standard library
Before Kotlin 1.4.0, you could apply SAM (Single Abstract Method) conversions only when working with Java
methods and Java interfaces from Kotlin. From now on, you can use SAM conversions for Kotlin interfaces as
well. To do so, mark a Kotlin interface explicitly as functional with the fun modi er.
40
SAM conversion applies if you pass a lambda as an argument when an interface with only one single
abstract method is expected as a parameter. In this case, the compiler automatically converts the lambda to
an instance of the class that implements the abstract member function.
fun interface IntPredicate {
fun accept(i: Int): Boolean
}
fun main() {
println("Is 7 even? - ${isEven.accept(7)}")
}
Kotlin compiler o ers explicit API mode for library authors. In this mode, the compiler performs additional
checks that help make the library’s API clearer and more consistent. It adds the following requirements for
declarations exposed to the library’s public API:
— Visibility modi ers are required for declarations if the default visibility exposes them to the public API.
This helps ensure that no declarations are exposed to the public API unintentionally.
— Explicit type speci cations are required for properties and functions that are exposed to the public API.
This guarantees that API users are aware of the types of API members they use.
Depending on your con guration, these explicit APIs can produce errors (strict mode) or warnings (warning
mode). Certain kinds of declarations are excluded from such checks for the sake of readability and common
sense:
— primary constructors
— properties of data classes
— property getters and setters
— override methods
To compile your module in the explicit API mode, add the following lines to your Gradle build script:
kotlin {
// for strict mode
explicitApi()
// or
explicitApi = 'strict'
41
kotlin {
// for strict mode
explicitApi()
// or
explicitApi = ExplicitApiMode.Strict
When using the command-line compiler, switch to explicit API mode by adding the -Xexplicit-api
compiler option with the value strict or warning .
-Xexplicit-api={strict|warning}
For more details about the explicit API mode, see the KEEP.
In Kotlin 1.3, when you called a function with named arguments, you had to place all the arguments without
names (positional arguments) before the rst named argument. For example, you could call f(1, y = 2) ,
but you couldn't call f(x = 1, 2) .
It was really annoying when all the arguments were in their correct positions but you wanted to specify a
name for one argument in the middle. It was especially helpful for making absolutely clear which attribute a
boolean or null value belongs to.
In Kotlin 1.4, there is no such limitation – you can now specify a name for an argument in the middle of a set
of positional arguments. Moreover, you can mix positional and named arguments any way you like, as long
as they remain in the correct order.
fun reformat(
str: String,
uppercaseFirstLetter: Boolean = true,
wordSeparator: Char = ' '
) {
// ...
}
Trailing comma
With Kotlin 1.4 you can now add a trailing comma in enumerations such as argument and parameter lists,
when entries, and components of destructuring declarations. With a trailing comma, you can add new items
and change their order without adding or removing commas.
This is especially helpful if you use multi-line syntax for parameters or values. After adding a trailing comma,
you can then easily swap lines with parameters or values.
fun reformat(
str: String,
uppercaseFirstLetter: Boolean = true,
wordSeparator: Character = ' ', //trailing comma
) {
// ...
}
42
val colors = listOf(
"red",
"green",
"blue", //trailing comma
)
Now you can use callable references to functions with default argument values. If the callable reference to
the function foo takes no arguments, the default value 0 is used.
fun foo(i: Int = 0): String = "$i!"
fun main() {
println(apply(::foo))
}
Previously, you had to write additional overloads for the function apply to use the default argument
values.
// some new overload
fun applyInt(func: (Int) -> String): String = func(0)
In Kotlin 1.4, you can use callable references to functions returning any type in Unit -returning functions.
Before Kotlin 1.4, you could only use lambda arguments in this case. Now you can use both lambda
arguments and callable references.
fun foo(f: () -> Unit) { }
fun returnsInt(): Int = 42
fun main() {
foo { returnsInt() } // this was the only way to do it before 1.4
foo(::returnsInt) // starting from 1.4, this also works
}
Now you can adapt callable references to functions when passing a variable number of arguments
( vararg ) . You can pass any number of parameters of the same type at the end of the list of passed
arguments.
43
fun foo(x: Int, vararg y: String) {}
fun test() {
use0(::foo)
use1(::foo)
use2(::foo)
}
In addition to suspend conversion on lambdas, Kotlin now supports suspend conversion on callable
references starting from version 1.4.0.
fun call() {}
fun takeSuspend(f: suspend () -> Unit) {}
fun test() {
takeSuspend { call() } // OK before 1.4
takeSuspend(::call) // In Kotlin 1.4, it also works
}
In Kotlin 1.3, you could not use unquali ed break and continue inside when expressions included in
loops. The reason was that these keywords were reserved for possible fall-through behavior in when
expressions.
That’s why if you wanted to use break and continue inside when expressions in loops, you had to label
them, which became rather cumbersome.
fun test(xs: List<Int>) {
LOOP@for (x in xs) {
when (x) {
2 -> continue@LOOP
17 -> break@LOOP
else -> println(x)
}
}
}
In Kotlin 1.4, you can use break and continue without labels inside when expressions included in loops.
They behave as expected by terminating the nearest enclosing loop or proceeding to its next step.
fun test(xs: List<Int>) {
for (x in xs) {
when (x) {
2 -> continue
17 -> break
else -> println(x)
}
}
}
Back to top
44
With Kotlin 1.4, you can use the new tools in IntelliJ IDEA to simplify Kotlin development:
With the exible new Kotlin Project Wizard, you have a place to easily create and con gure di erent types of
Kotlin projects, including multiplatform projects, which can be di cult to con gure without a UI.
1. Select the project template, depending on what you’re trying to do. More templates will be added in the future.
2. Select the build system – Gradle (Kotlin or Groovy DSL), Maven, or IntelliJ IDEA.
The Kotlin Project Wizard will only show the build systems supported on the selected project template.
3. Preview the project structure directly on the main screen.
Then you can nish creating your project or, optionally, con gure the project on the next screen:
In the future, we are going to make the Kotlin Project Wizard even more exible by adding more
con guration options and templates.
You can try out the new Kotlin Project Wizard by working through these tutorials:
Coroutine Debugger
Many people already use coroutines for asynchronous programming. But when it came to debugging,
working with coroutines before Kotlin 1.4, could be a real pain. Since coroutines jumped between threads, it
was di cult to understand what a speci c coroutine was doing and check its context. In some cases, tracking
steps over breakpoints simply didn’t work. As a result, you had to rely on logging or mental e ort to debug
code that used coroutines.
In Kotlin 1.4, debugging coroutines is now much more convenient with the new functionality shipped with
the Kotlin plugin.
The Debug Tool Window now contains a new Coroutines tab. In this tab, you can nd information about
both currently running and suspended coroutines. The coroutines are grouped by the dispatcher they are
running on.
45
— See the values of local and captured variables for both running and suspended coroutines.
— See a full coroutine creation stack, as well as a call stack inside the coroutine. The stack includes all
frames with variable values, even those that would be lost during standard debugging.
If you need a full report containing the state of each coroutine and its stack, right-click inside the
Coroutines tab, and then click Get Coroutines Dump. Currently, the coroutines dump is rather simple, but
we’re going to make it more readable and helpful in future versions of Kotlin.
Learn more about debugging coroutines in this blog post and IntelliJ IDEA documentation.
Back to top
New compiler
The new Kotlin compiler is going to be really fast; it will unify all the supported platforms and provide an API
for compiler extensions. It's a long-term project, and we've already completed several steps in Kotlin 1.4.0:
Kotlin 1.4 uses a new, more powerful type inference algorithm. This new algorithm was already available to
try in Kotlin 1.3 by specifying a compiler option, and now it’s used by default. You can nd the full list of
issues xed in the new algorithm in YouTrack. Here you can nd some of the most noticeable
improvements:
The new inference algorithm infers types for many cases where the old algorithm required you to specify
them explicitly. For instance, in the following example the type of the lambda parameter it is correctly
inferred to String? :
//sampleStart
val rulesMap: Map<String, (String?) -> Boolean> = mapOf(
"weak" to { it != null },
"medium" to { !it.isNullOrBlank() },
"strong" to { it != null && "^[a-zA-Z0-9]+$".toRegex().matches(it) }
)
//sampleEnd
fun main() {
println(rulesMap.getValue("weak")("abc!"))
println(rulesMap.getValue("strong")("abc"))
println(rulesMap.getValue("strong")("abc!"))
}
46
In Kotlin 1.3, you needed to introduce an explicit lambda parameter or replace to with a Pair constructor
with explicit generic arguments to make it work.
In Kotlin 1.3, the last expression inside a lambda wasn’t smart cast unless you speci ed the expected type.
Thus, in the following example, Kotlin 1.3 infers String? as the type of the result variable:
val result = run {
var str = currentValue()
if (str == null) {
str = "test"
}
str // the Kotlin compiler knows that str is not null here
}
// The type of 'result' is String? in Kotlin 1.3 and String in Kotlin 1.4
In Kotlin 1.4, thanks to the new inference algorithm, the last expression inside a lambda gets smart cast, and
this new, more precise type is used to infer the resulting lambda type. Thus, the type of the result
variable becomes String .
In Kotlin 1.3, you often needed to add explicit casts (either !! or type casts like as String ) to make such
cases work, and now these casts have become unnecessary.
In Kotlin 1.3, you couldn’t access a member reference of a smart cast type. Now in Kotlin 1.4 you can:
import kotlin.reflect.KFunction
//sampleStart
fun perform(animal: Animal) {
val kFunction: KFunction<*> = when (animal) {
is Cat -> animal::meow
is Dog -> animal::woof
}
kFunction.call()
}
//sampleEnd
fun main() {
perform(Cat())
}
You can use di erent member references animal::meow and animal::woof after the animal variable
has been smart cast to speci c types Cat and Dog . After type checks, you can access member references
corresponding to subtypes.
47
The type of a delegated property wasn’t taken into account while analyzing the delegate expression which
follows the by keyword. For instance, the following code didn’t compile before, but now the compiler
correctly infers the types of the old and new parameters as String? :
import kotlin.properties.Delegates
fun main() {
var prop: String? by Delegates.observable(null) { p, old, new ->
println("$old → $new")
}
prop = "abc"
prop = "xyz"
}
Kotlin has supported SAM conversions for Java interfaces from the beginning, but there was one case that
wasn’t supported, which was sometimes annoying when working with existing Java libraries. If you called a
Java method that took two SAM interfaces as parameters, both arguments needed to be either lambdas or
regular objects. You couldn't pass one argument as a lambda and another as an object.
The new algorithm xes this issue, and you can pass a lambda instead of a SAM interface in any case, which
is the way you’d naturally expect it to work.
// FILE: A.java
public class A {
public static void foo(Runnable r1, Runnable r2) {}
}
// FILE: test.kt
fun test(r1: Runnable) {
A.foo(r1) {} // Works in Kotlin 1.4
}
In Kotlin 1.4, you can use Java SAM interfaces in Kotlin and apply SAM conversions to them.
import java.lang.Runnable
fun test() {
foo { } // OK
}
In Kotlin 1.3, you would have had to declare the function foo above in Java code to perform a SAM
conversion.
In Kotlin, we have three backends that generate executables: Kotlin/JVM, Kotlin/JS, and Kotlin/Native.
Kotlin/JVM and Kotlin/JS don't share much code since they were developed independently of each other.
Kotlin/Native is based on a new infrastructure built around an intermediate representation (IR) for Kotlin
code.
We are now migrating Kotlin/JVM and Kotlin/JS to the same IR. As a result, all three backends share a lot of
logic and have a uni ed pipeline. This allows us to implement most features, optimizations, and bug xes
only once for all platforms. Both new IR-based back-ends are in Alpha.
48
A common backend infrastructure also opens the door for multiplatform compiler extensions. You will be
able to plug into the pipeline and add custom processing and transformations that will automatically work
for all platforms.
We encourage you to use our new JVM IR and JS IR backends, which are currently in Alpha, and share your
feedback with us.
Back to top
Kotlin/JVM
Kotlin 1.4.0 includes a number of JVM-speci c improvements, such as:
Along with Kotlin/JS, we are migrating Kotlin/JVM to the uni ed IR backend, which allows us to implement
most features and bug xes once for all platforms. You will also be able to bene t from this by creating
multiplatform extensions that will work for all platforms.
Kotlin 1.4.0 does not provide a public API for such extensions yet, but we are working closely with our
partners, including Jetpack Compose, who are already building their compiler plugins using our new
backend.
We encourage you to try out the new Kotlin/JVM backend, which is currently in Alpha, and to le any issues
and feature requests to our issue tracker. This will help us to unify the compiler pipelines and bring
compiler extensions like Jetpack Compose to the Kotlin community more quickly.
To enable the new JVM IR backend, specify an additional compiler option in your Gradle build script:
kotlinOptions.useIR = true
If you enable Jetpack Compose, you will automatically be opted in to the new JVM backend without
needing to specify the compiler option in kotlinOptions.
When using the command-line compiler, add the compiler option -Xuse-ir .
You can use code compiled by the new JVM IR backend only if you've enabled the new backend.
Otherwise, you will get an error. Considering this, we don't recommend that library authors switch
to the new backend in production.
49
When compiling Kotlin code to targets JVM 1.8 and above, you could compile non-abstract methods of Kotlin
interfaces into Java's default methods. For this purpose, there was a mechanism that includes the
@JvmDefault annotation for marking such methods and the -Xjvm-default compiler option that
enables processing of this annotation.
In 1.4.0, we've added a new mode for generating default methods: -Xjvm-default=all compiles all non-
abstract methods of Kotlin interfaces to default Java methods. For compatibility with the code that uses
the interfaces compiled without default , we also added all-compatibility mode.
For more information about default methods in the Java interop, see the documentation and this blog post.
Starting from Kotlin 1.4.0, all runtime null checks will throw a java.lang.NullPointerException
instead of KotlinNullPointerException , IllegalStateException ,
IllegalArgumentException , and TypeCastException . This applies to: the !! operator, parameter
null checks in the method preamble, platform-typed expression null checks, and the as operator with a
non-null type. This doesn’t apply to lateinit null checks and explicit library function calls like
checkNotNull or requireNotNull .
This change increases the number of possible null check optimizations that can be performed either by the
Kotlin compiler or by various kinds of bytecode processing tools, such as the Android R8 optimizer.
Note that from a developer’s perspective, things won’t change that much: the Kotlin code will throw
exceptions with the same error messages as before. The type of exception changes, but the information
passed stays the same.
Kotlin can now generate type annotations in the JVM bytecode (target version 1.8+), so that they become
available in Java re ection at runtime. To emit the type annotation in the bytecode, follow these steps:
1. Make sure that your declared annotation has a proper annotation target (Java’s ElementType.TYPE_USE or Kotlin’s
AnnotationTarget.TYPE ) and retention ( AnnotationRetention.RUNTIME ).
2. Compile the annotation class declaration to JVM bytecode target version 1.8+. You can specify it with -jvm-target=1.8
compiler option.
3. Compile the code that uses the annotation to JVM bytecode target version 1.8+ ( -jvm-target=1.8 ) and add the -Xemit-jvm-
type-annotations compiler option.
Note that the type annotations from the standard library aren’t emitted in the bytecode for now because
the standard library is compiled with the target version 1.6.
— Type annotations on method parameters, method return types and property types;
— Invariant projections of type arguments, such as Smth<@Ann Foo> , Array<@Ann Foo> .
In the following example, the @Foo annotation on the String type can be emitted to the bytecode and
then used by the library code:
50
@Target(AnnotationTarget.TYPE)
annotation class Foo
class A {
fun foo(): @Foo String = "OK"
}
Back to top
Kotlin/JS
On the JS platform, Kotlin 1.4.0 provides the following improvements:
The kotlin.js Gradle plugin comes with an adjusted Gradle DSL, which provides a number of new
con guration options and is more closely aligned to the DSL used by the kotlin-multiplatform plugin.
Some of the most impactful changes include:
— Explicit toggles for the creation of executable les via binaries.executable() . Read more about the
executing Kotlin/JS and its environment here.
— Con guration of webpack's CSS and style loaders from within the Gradle con guration via cssSupport .
Read more about using them here.
— Improved management for npm dependencies, with mandatory version numbers or semver version
ranges, as well as support for development, peer, and optional npm dependencies using devNpm ,
optionalNpm and peerNpm . Read more about dependency management for npm packages directly
from Gradle here.
— Stronger integrations for Dukat, the generator for Kotlin external declarations. External declarations can
now be generated at build time, or can be manually generated via a Gradle task. Read more about how
to use the integration here.
New JS IR backend
The IR backend for Kotlin/JS, which currently has Alpha stability, provides some new functionality speci c to
the Kotlin/JS target which is focused around the generated code size through dead code elimination, and
improved interoperation with JavaScript and TypeScript, among others.
To enable the Kotlin/JS IR backend, set the key kotlin.js.compiler=ir in your gradle.properties ,
or pass the IR compiler type to the js function of your Gradle build script:
kotlin {
js(IR) { // or: LEGACY, BOTH
// . . .
}
binaries.executable()
}
For more detailed information about how to con gure the Kotlin/JS IR compiler backend, check out the
documentation.
51
With the new @JsExport annotation and the ability to generate TypeScript de nitions from Kotlin code,
the Kotlin/JS IR compiler backend improves JavaScript & TypeScript interoperability. This also makes it easier
to integrate Kotlin/JS code with existing tooling, to create hybrid applications and leverage code-sharing
functionality in multiplatform projects.
Learn more about the available features in the Kotlin/JS IR compiler backend in the documentation.
Back to top
Kotlin/Native
In 1.4.0, Kotlin/Native got a signi cant number of new features and improvements, including:
— Performance improvements
— Simpli ed management of CocoaPods dependencies
In 1.4.0, we add the basic support for suspending functions in Swift and Objective-C. Now, when you compile
a Kotlin module into an Apple framework, suspending functions are available in it as functions with callbacks
( completionHandler in the Swift/Objective-C terminology). When you have such functions in the
generated framework’s header, you can call them from your Swift or Objective-C code and even override
them.
For more information about using suspending functions in Swift and Objective-C, see the documentation.
Previous versions of Kotlin provided experimental support for generics in Objective-C interop. Since 1.4.0,
Kotlin/Native generates Apple frameworks with generics from Kotlin code by default. In some cases, this
may break existing Objective-C or Swift code calling Kotlin frameworks. To have the framework header
written without generics, add the -Xno-objc-generics compiler option.
52
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries.all {
freeCompilerArgs += "-Xno-objc-generics"
}
}
}
Please note that all speci cs and limitations listed in the documentation are still valid.
In 1.4.0, we slightly change the Swift API generated from Kotlin with respect to the way exceptions are
translated. There is a fundamental di erence in error handling between Kotlin and Swift. All Kotlin
exceptions are unchecked, while Swift has only checked errors. Thus, to make Swift code aware of expected
exceptions, Kotlin functions should be marked with a @Throws annotation specifying a list of potential
exception classes.
When compiling to Swift or the Objective-C framework, functions that have or are inheriting @Throws
annotation are represented as NSError* -producing methods in Objective-C and as throws methods in
Swift.
Previously, any exceptions other than RuntimeException and Error were propagated as NSError .
Now this behavior changes: now NSError is thrown only for exceptions that are instances of classes
speci ed as parameters of @Throws annotation (or their subclasses). Other Kotlin exceptions that reach
Swift/Objective-C are considered unhandled and cause program termination.
Starting with 1.4.0, the Kotlin/Native compiler produces debug symbol les ( .dSYM s) for release binaries
on Darwin platforms by default. This can be disabled with the -Xadd-light-debug=disable compiler
option. On other platforms, this option is disabled by default. To toggle this option in Gradle, use:
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries.all {
freeCompilerArgs += "-Xadd-light-debug={enable|disable}"
}
}
}
For more information about crash report symbolication, see the documentation.
Performance improvements
Kotlin/Native has received a number of performance improvements that speed up both the development
process and execution. Here are some examples:
— To improve the speed of object allocation, we now o er the mimalloc memory allocator as an alternative
to the system allocator. mimalloc works up to two times faster on some benchmarks. Currently, the
usage of mimalloc in Kotlin/Native is experimental; you can switch to it using the -
Xallocator=mimalloc compiler option.
— We’ve reworked how C interop libraries are built. With the new tooling, Kotlin/Native produces interop
libraries up to 4 times as fast as before, and artifacts are 25% to 30% the size they used to be.
53
— Overall runtime performance has improved because of optimizations in GC. This improvement will be
especially apparent in projects with a large number of long-lived objects. HashMap and HashSet
collections now work faster by escaping redundant boxing.
— In 1.3.70 we introduced two new features for improving the performance of Kotlin/Native compilation:
caching project dependencies and running the compiler from the Gradle daemon. Since that time, we’ve
managed to x numerous issues and improve the overall stability of these features.
Previously, once you integrated your project with the dependency manager CocoaPods, you could build an
iOS, macOS, watchOS, or tvOS part of your project only in Xcode, separate from other parts of your
multiplatform project. These other parts could be built in Intellij IDEA.
Moreover, every time you added a dependency on an Objective-C library stored in CocoaPods (Pod library),
you had to switch from IntelliJ IDEA to Xcode, call pod install , and run the Xcode build there.
Now you can manage Pod dependencies right in Intellij IDEA while enjoying the bene ts it provides for
working with code, such as code highlighting and completion. You can also build the whole Kotlin project
with Gradle, without having to switch to Xcode. This means you only have to go to Xcode when you need to
write Swift/Objective-C code or run your application on a simulator or device.
Now you can also work with Pod libraries stored locally.
— A Kotlin project and Pod libraries stored remotely in the CocoaPods repository or stored locally on your
machine.
— A Kotlin Pod (Kotlin project used as a CocoaPods dependency) and an Xcode project with one or more
targets.
Complete the initial con guration, and when you add a new dependency to cocoapods , just re-import the
project in IntelliJ IDEA. The new dependency will be added automatically. No additional steps are required.
Back to top
Kotlin Multiplatform
Multiplatform projects are in Alpha. Language features and tooling may change in future Kotlin
versions.
Kotlin Multiplatform reduces time spent writing and maintaining the same code for di erent platforms
while retaining the exibility and bene ts of native programming. We continue investing our e ort in
multiplatform features and improvements:
54
Multiplatform projects require Gradle 6.0 or later.
With the new hierarchical project structure support, you can share code among several platforms in a
multiplatform project.
Previously, any code added to a multiplatform project could be placed either in a platform-speci c source
set, which is limited to one target and can’t be reused by any other platform, or in a common source set, like
commonMain or commonTest , which is shared across all the platforms in the project. In the common
source set, you could only call a platform-speci c API by using an expect declaration that needs platform-
speci c actual implementations.
This made it easy to share code on all platforms, but it was not so easy to share between only some of the
targets, especially similar ones that could potentially reuse a lot of the common logic and third-party APIs.
For example, in a typical multiplatform project targeting iOS, there are two iOS-related targets: one for iOS
ARM64 devices, and the other for the x64 simulator. They have separate platform-speci c source sets, but in
practice, there is rarely a need for di erent code for the device and simulator, and their dependencies are
much alike. So iOS-speci c code could be shared between them.
Apparently, in this setup, it would be desirable to have a shared source set for two iOS targets, with
Kotlin/Native code that could still directly call any of the APIs that are common to both the iOS device and
the simulator.
Now you can do this with the hierarchical project structure support, which infers and adapts the API and
language features available in each source set based on which targets consume them.
For common combinations of targets, you can create a hierarchical structure with target shortcuts.
For example, create two iOS targets and the shared source set shown above with the ios() shortcut:
kotlin {
ios() // iOS device and simulator targets; iosMain and iosTest source sets
}
For other combinations of targets, create a hierarchy manually by connecting the source sets with the
dependsOn relation.
55
kotlin {
sourceSets {
desktopMain {
dependsOn(commonMain)
}
linuxX64Main {
dependsOn(desktopMain)
}
mingwX64Main {
dependsOn(desktopMain)
}
macosX64Main {
dependsOn(desktopMain)
}
}
}
kotlin{
sourceSets {
val desktopMain by creating {
dependsOn(commonMain)
}
val linuxX64Main by getting {
dependsOn(desktopMain)
}
val mingwX64Main by getting {
dependsOn(desktopMain)
}
val macosX64Main by getting {
dependsOn(desktopMain)
}
}
}
Thanks to the hierarchical project structure, libraries can also provide common APIs for a subset of targets.
Learn more about sharing code in libraries.
You can use platform-dependent libraries, such as Foundation , UIKit , and posix , in source sets
shared among several native targets. This can help you share more native code without being limited by
platform-speci c dependencies.
No additional steps are required – everything is done automatically. IntelliJ IDEA will help you detect
common declarations that you can use in the shared code.
From now on, instead of specifying dependencies on di erent variants of the same library in shared and
platform-speci c source sets where it is used, you should specify a dependency only once in the shared
source set.
56
kotlin {
sourceSets {
commonMain {
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
}
}
}
}
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2")
}
}
}
}
Don’t use kotlinx library artifact names with su xes specifying the platform, such as -common , -native ,
or similar, as they are NOT supported anymore. Instead, use the library base artifact name, which in the
example above is kotlinx-coroutines-core .
— The stdlib library – starting from Kotlin 1.4.0, the stdlib dependency is added automatically.
— The kotlin.test library – you should still use test-common and test-annotations-common .
These dependencies will be addressed later.
If you need a dependency only for a speci c platform, you can still use platform-speci c variants of standard
and kotlinx libraries with such su xes as -jvm or -js , for example kotlinx-coroutines-core-jvm .
Back to top
You no longer need to declare a dependency on the stdlib library in any Kotlin Gradle project, including
a multiplatform one. The dependency is added by default.
The automatically added standard library will be the same version of the Kotlin Gradle plugin, since they
have the same versioning.
57
For platform-speci c source sets, the corresponding platform-speci c variant of the library is used, while a
common standard library is added to the rest. The Kotlin Gradle plugin will select the appropriate JVM
standard library depending on the kotlinOptions.jvmTarget compiler option of your Gradle build
script.
To enjoy the new features in your Kotlin projects, update Gradle to the latest version. Multiplatform projects
require Gradle 6.0 or later, while other Kotlin projects work with Gradle 5.4 or later.
In 1.4.0, we continued improving the IDE support for Gradle Kotlin DSL scripts ( *.gradle.kts les). Here
is what the new version brings:
— Explicit loading of script con gurations for better performance. Previously, the changes you make to the
build script were loaded automatically in the background. To improve the performance, we've disabled
the automatic loading of build script con guration in 1.4.0. Now the IDE loads the changes only when you
explicitly apply them.
In Gradle versions earlier than 6.0, you need to manually load the script con guration by clicking Load
Con guration in the editor.
In Gradle 6.0 and above, you can explicitly apply changes by clicking Load Gradle Changes or by
reimporting the Gradle project.
We’ve added one more action in IntelliJ IDEA 2020.1 with Gradle 6.0 and above – Load Script
Con gurations, which loads changes to the script con gurations without updating the whole project.
This takes much less time than reimporting the whole project.
You should also Load Script Con gurations for newly created scripts or when you open a project with
new Kotlin plugin for the rst time.
With Gradle 6.0 and above, you are now able to load all scripts at once as opposed to the previous
implementation where they were loaded individually. Since each request requires the Gradle
con guration phase to be executed, this could be resource-intensive for large Gradle projects.
— Better error reporting. Previously you could only see errors from the Gradle Daemon in separate log les.
Now the Gradle Daemon returns all the information about errors directly and shows it in the Build tool
window. This saves you both time and e ort.
Back to top
Standard library
58
Here is the list of the most signi cant changes to the Kotlin standard library in 1.4.0:
The following API elements have been moved to the common library:
Collections
In 1.4.0, the standard library includes a number of useful functions for working with collections:
— setOfNotNull() , which makes a set consisting of all the non-null items among the provided
arguments.
fun main() {
//sampleStart
val set = setOfNotNull(null, 1, 2, 0, null)
println(set)
//sampleEnd
}
fun main() {
59
fun main() {
//sampleStart
val numbers = (0 until 50).asSequence()
val result = numbers.map { it * 2 }.shuffled().take(5)
println(result.toList()) //five random even numbers below 100
//sampleEnd
}
— *Indexed() counterparts for onEach() and flatMap() . The operation that they apply to the
collection elements has the element index as a parameter.
fun main() {
//sampleStart
listOf("a", "b", "c", "d").onEachIndexed {
index, item -> println(index.toString() + ":" + item)
}
fun main() {
//sampleStart
val empty = emptyList<Int>()
empty.reduceOrNull { a, b -> a + b }
//empty.reduce { a, b -> a + b } // Exception: Empty collection can't be
reduced.
//sampleEnd
}
— runningFold() , its synonym scan() , and runningReduce() apply the given operation to the
collection elements sequentially, similarly to fold() and reduce() ; the di erence is that these new
functions return the whole sequence of intermediate results.
fun main() {
//sampleStart
val numbers = mutableListOf(0, 1, 2, 3, 4, 5)
val runningReduceSum = numbers.runningReduce { sum, item -> sum + item }
val runningFoldSum = numbers.runningFold(10) { sum, item -> sum + item }
//sampleEnd
println(runningReduceSum.toString())
println(runningFoldSum.toString())
}
— sumOf() takes a selector function and returns a sum of its values for all elements of a collection.
sumOf() can produce sums of the types Int , Long , Double , UInt , and ULong . On the JVM,
BigInteger and BigDecimal are also available.
data class OrderItem(val name: String, val price: Double, val count: Int)
60
data class OrderItem(val name: String, val price: Double, val count: Int)
fun main() {
//sampleStart
val order = listOf<OrderItem>(
OrderItem("Cake", price = 10.0, count = 1),
OrderItem("Coffee", price = 2.5, count = 3),
OrderItem("Tea", price = 1.5, count = 2))
— The min() and max() functions have been renamed to minOrNull() and maxOrNull() to comply
with the naming convention used across the Kotlin collections API. An *OrNull su x in the function
name means that it returns null if the receiver collection is empty. The same applies to minBy() ,
maxBy() , minWith() , maxWith() – in 1.4, they have *OrNull() synonyms.
— The new minOf() and maxOf() extension functions return the minimum and the maximum value of
the given selector function on the collection items.
data class OrderItem(val name: String, val price: Double, val count: Int)
fun main() {
//sampleStart
val order = listOf<OrderItem>(
OrderItem("Cake", price = 10.0, count = 1),
OrderItem("Coffee", price = 2.5, count = 3),
OrderItem("Tea", price = 1.5, count = 2))
val highestPrice = order.maxOf { it.price }
//sampleEnd
println("The most expensive item in the order costs $highestPrice")
}
There are also minOfWith() and maxOfWith() , which take a Comparator as an argument, and
*OrNull() versions of all four functions that return null on empty collections.
— New overloads for flatMap and flatMapTo let you use transformations with return types that don’t
match the receiver type, namely:
— Transformations to Sequence on Iterable , Array , and Map
fun main() {
//sampleStart
val list = listOf("kot", "lin")
val lettersList = list.flatMap { it.asSequence() }
val lettersSeq = list.asSequence().flatMap { it.toList() }
//sampleEnd
println(lettersList)
println(lettersSeq.toList())
}
— removeFirst() and removeLast() shortcuts for removing elements from mutable lists, and
*orNull() counterparts of these functions.
Arrays
To provide a consistent experience when working with di erent container types, we’ve also added new
functions for arrays:
61
— shuffle() puts the array elements in a random order.
— onEach() performs the given action on each array element and returns the array itself.
— associateWith() and associateWithTo() build maps with the array elements as keys.
— 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() and sortWith() for array subranges are now available in the common library.
fun main() {
//sampleStart
var language = ""
val letters = arrayOf("k", "o", "t", "l", "i", "n")
val fileExt = letters.onEach { language += it }
.filterNot { it in "aeuio" }.take(2)
.joinToString(prefix = ".", separator = "")
println(language) // "kotlin"
println(fileExt) // ".kt"
letters.shuffle()
letters.reverse(0, 3)
letters.sortDescending(2, 5)
println(letters.contentToString()) // [k, o, t, l, i, n]
//sampleEnd
}
Additionally, there are new functions for conversions between CharArray / ByteArray and String :
fun main() {
//sampleStart
val str = "kotlin"
val array = str.toCharArray()
println(array.concatToString())
//sampleEnd
}
ArrayDeque
We've also added the ArrayDeque class – an implementation of a double-ended queue. Double-ended
queue lets you can add or remove elements both at the beginning and the end of the queue in an
amortized constant time. You can use a double-ended queue by default when you need a queue or a stack
in your code.
fun main() {
val deque = ArrayDeque(listOf(1, 2, 3))
deque.addFirst(0)
deque.addLast(4)
println(deque) // [0, 1, 2, 3, 4]
println(deque.first()) // 0
println(deque.last()) // 4
deque.removeFirst()
deque.removeLast()
println(deque) // [1, 2, 3]
}
62
The ArrayDeque implementation uses a resizable array underneath: it stores the contents in a circular
bu er, an Array , and resizes this Array only when it becomes full.
The standard library in 1.4.0 includes a number of improvements in the API for string manipulation:
— Some existing functions of StringBuilder are available in the common library. Among them are
append() , insert() , substring() , setLength() , and more.
— New functions Appendable.appendLine() and StringBuilder.appendLine() have been added
to the common library. They replace the JVM-only appendln() functions of these classes.
fun main() {
//sampleStart
println(buildString {
appendLine("Hello,")
appendLine("world")
})
//sampleEnd
}
Bit operations
— countOneBits()
— countLeadingZeroBits()
— countTrailingZeroBits()
— takeHighestOneBit()
— takeLowestOneBit()
fun main() {
//sampleStart
val number = "1010000".toInt(radix = 2)
println(number.countOneBits())
println(number.countTrailingZeroBits())
println(number.takeHighestOneBit().toString(2))
//sampleEnd
}
63
In 1.4.0, we have added new features to improve your experience with delegated properties in Kotlin:
— ReadWriteProperty now extends ReadOnlyProperty so you can use both of them for read-only
properties.
Aside from the new API, we've made some optimizations that reduce the resulting bytecode size. These
optimizations are described in this blog post.
A new extension property KType.javaType (currently experimental) in the stdlib helps you obtain a
java.lang.reflect.Type from a Kotlin type without using the whole kotlin-reflect dependency.
import kotlin.reflect.javaType
import kotlin.reflect.typeOf
@OptIn(ExperimentalStdlibApi::class)
inline fun <reified T> accessReifiedTypeArg() {
val kType = typeOf<T>()
println("Kotlin type: $kType")
println("Java type: ${kType.javaType}")
}
@OptIn(ExperimentalStdlibApi::class)
fun main() {
accessReifiedTypeArg<String>()
// Kotlin type: kotlin.String
// Java type: class java.lang.String
accessReifiedTypeArg<List<String>>()
// Kotlin type: kotlin.collections.List<kotlin.String>
// Java type: java.util.List<java.lang.String>
}
Starting from 1.4.0, we have embedded Proguard/R8 con gurations for Kotlin Re ection in kotlin-
reflect.jar . With this in place, most Android projects using R8 or Proguard should work with kotlin-
re ect without needing any additional con guration. You no longer need to copy-paste the Proguard rules
for kotlin-re ect internals. But note that you still need to explicitly list all the APIs you’re going to re ect on.
— NaN , NEGATIVE_INFINITY , and POSITIVE_INFINITY in Double and Float are now de ned as
const , so you can use them as annotation arguments.
64
— New constants SIZE_BITS and SIZE_BYTES in Double and Float contain the number of bits and
bytes used to represent an instance of the type in binary form.
— The maxOf() and minOf() top-level functions can accept a variable number of arguments ( vararg ).
Kotlin 1.4.0 adds module-info.java module information to default standard library artifacts. This lets
you use them with jlink tool, which generates custom Java runtime images containing only the platform
modules that are required for your app. You could already use jlink with Kotlin standard library artifacts, but
you had to use separate artifacts to do so – the ones with the “modular” classi er – and the whole setup
wasn’t straightforward.
In Android, make sure you use the Android Gradle plugin version 3.2 or higher, which can correctly process
jar les with module-info.
Deprecations
We've deprecated the functions toShort() and toByte() on Double and Float because they could
lead to unexpected results because of the narrow value range and smaller variable size.
To convert oating-point numbers to Byte or Short , use the two-step conversion: rst, convert them to
Int , and then convert them again to the target type.
We've deprecated the min() and max() collection functions in favor of minOrNull() and
maxOrNull() , which more properly re ect their behavior – returning null on empty collections. See this
issue for details.
Back to top
65
With Kotlin 1.4.0, we are shipping the rst stable version of kotlinx.serialization - 1.0.0-RC. Now we are
pleased to declare the JSON serialization API in kotlinx-serialization-core (previously known as
kotlinx-serialization-runtime ) stable. Libraries for other serialization formats remain
experimental, along with some advanced parts of the core library.
We have signi cantly reworked the API for JSON serialization to make it more consistent and easier to use.
From now on, we'll continue developing the JSON serialization API in a backward-compatible manner.
However, if you have used previous versions of it, you'll need to rewrite some of your code when migrating
to 1.0.0-RC. To help you with this, we also o er the Kotlin Serialization Guide – the complete set of
documentation for kotlinx.serialization . It will guide you through the process of using the most
important features and it can help you address any issues that you might face.
Note: kotlinx-serialization 1.0.0-RC only works with Kotlin compiler 1.4. Earlier compiler
versions are not compatible.
Back to top
To help you become more familiar with scripting in Kotlin, we’ve prepared a project with examples. It
contains examples of the standard scripts ( *.main.kts ) and examples of uses of the Kotlin Scripting API
and custom script de nitions. Please give it a try and share your feedback using our issue tracker.
In 1.4.0, we’ve introduced a new API for resolving external dependencies (such as Maven artifacts), along
with implementations for it. This API is published in the new artifacts kotlin-scripting-dependencies
and kotlin-scripting-dependencies-maven . The previous dependency resolution functionality in
kotlin-script-util library is now deprecated.
The new experimental REPL API is now a part of the Kotlin Scripting API. There are also several
implementations of it in the published artifacts, and some have advanced functionality, such as code
completion. We use this API in the Kotlin Jupyter kernel and now you can try it in your own custom shells
and REPLs.
66
The Kotlin Scripting API now provides the ability to implement a compiled scripts cache, signi cantly
speeding up subsequent executions of unchanged scripts. Our default advanced script implementation
kotlin-main-kts already has its own cache.
Artifacts renaming
Back to top
Just change the Kotlin version to 1.4.0 and re-import your Gradle or Maven project. The IDE will then ask
you about migration.
If you agree, it will run migration code inspections that will check your code and suggest corrections for
anything that doesn't work or that is not recommended in 1.4.0.
Code inspections have di erent severity levels, to help you decide which suggestions to accept and which to
ignore.
To help you start using the new features of Kotlin multiplatform in existing projects, we publish the
migration guide for multiplatform projects.
Back to top
67
What's New in Kotlin 1.3
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 Re ection
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 exibility, and to make sharing common code easier. Also, Kotlin/Native is now supported as one of the
targets!
— In the old model, common and platform-speci c code needed to be placed in separate modules, linked
by expectedBy dependencies. Now, common and platform-speci c code is placed in di erent source
roots of the same module, making projects easier to con gure.
— There is now a large number of preset platform con gurations for di erent supported platforms.
— The dependencies con guration has been changed; dependencies are now speci ed 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.
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:
fun foo(s: String?) {
if (s != null) s.length // Compiler automatically casts 's' to 'String'
}
However, as soon as these checks are extracted in a separate function, all the smartcasts immediately
disappear:
fun String?.isNotNull(): Boolean = this != null
To improve the behavior in such cases, Kotlin 1.3 introduces experimental mechanism called contracts.
68
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:
fun require(condition: Boolean) {
// This is a syntax form which tells the compiler:
// "if this function returns successfully, then the passed 'condition' is true"
contract { returns() implies condition }
if (!condition) throw IllegalArgumentException(...)
}
fun foo() {
val x: Int
synchronize(lock) {
x = 42 // Compiler knows that lambda passed to 'synchronize' is called
// exactly once, so no reassignment is reported
}
println(x) // Compiler knows that lambda will be definitely called, performing
// initialization, so 'x' is considered to be initialized here
}
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 bene t from the improved analysis right now without
any additional opt-ins:
//sampleStart
fun bar(x: String?) {
if (!x.isNullOrEmpty()) {
println("length of '$x' is ${x.length}") // Yay, smartcast to not-null!
}
}
//sampleEnd
fun main() {
bar(null)
bar("42")
}
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.
69
Custom contracts are introduced by a call to contract stdlib function, which provides DSL scope:
fun String?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies (this@isNullOrEmpty != null)
}
return this == null || isEmpty()
}
See the details on the syntax as well as the compatibility notice in the KEEP.
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
fun sayHello() {
println("Hello, world!")
}
}
}
70
annotation class Foo {
enum class Direction { UP, DOWN, LEFT, RIGHT }
companion object {
fun foo(): Int = 42
val bar: Int = 42
}
}
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!
fun main() {
println("Hello, world!")
}
Kotlin 1.3 relaxes this limitation and adds support for functions with bigger arity:
fun trueEnterpriseComesToKotlin(block: (Any, Any, ... /* 42 more */, Any) -> Any) {
block(Any(), Any(), ..., Any())
}
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 ne, 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 xes arrive
immediately, making the code more safe and correct. So, Kotlin 1.3 introduces progressive compiler mode,
which can be enabled by passing the argument -progressive to the compiler.
In progressive mode, some xes in language semantics can arrive immediately. All these xes 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 ne 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.
71
Enabling the progressive mode can require you to rewrite some of your code, but it shouldn't be too much
— all the xes which are 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
Inline classes are available only since Kotlin 1.3 and currently are in Alpha. See details in the
reference.
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:
inline class Name(val s: String)
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:
inline class Name(val s: String)
//sampleStart
fun main() {
// In the next line no constructor call happens, and
// at the runtime 'name' contains just string "Kotlin"
val name = Name("Kotlin")
println(name.s)
}
//sampleEnd
Unsigned integers
Unsigned integers are available only since Kotlin 1.3 and currently are in Beta. See details in the
reference.
Most of the functionality of signed types are supported for unsigned counterparts too:
72
fun main() {
//sampleStart
// You can define unsigned types using literal suffixes
val uint = 42u
val ulong = 42uL
val ubyte: UByte = 255u
// You can convert signed types to unsigned and vice versa via stdlib extensions:
val int = uint.toInt()
val byte = ubyte.toByte()
val ulong2 = byte.toULong()
@JvmDefault
@JvmDefault is only available since Kotlin 1.3 and currently is experimental. See details in the
reference page.
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:
interface Foo {
// Will be generated as 'default' method
@JvmDefault
fun foo(): Int = 42
}
Warning! Annotating your API with @JvmDefault has serious implications on binary compatibility.
Make sure to carefully read the reference page before using @JvmDefault in production.
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 speci c solutions, like java.util.Random on JVM. This release xes this issue by
introducing the class kotlin.random.Random , which is available on all platforms:
73
import kotlin.random.Random
fun main() {
//sampleStart
val number = Random.nextInt(42) // number is in range [0, limit)
println(number)
//sampleEnd
}
isNullOrEmpty/orEmpty extensions
isNullOrEmpty and orEmpty extensions for some types are already present in stdlib . The rst 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.
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 e cient and easy to explore alternative:
keys.associateWith { getValue(it) } .
fun main() {
//sampleStart
val keys = 'a'..'f'
val map = keys.associateWith { it.toString().repeat(5).capitalize() }
map.forEach { println(it) }
//sampleEnd
}
74
fun main() {
//sampleStart
fun printAllUppercase(data: List<String>) {
val result = data
.filter { it.all { c -> c.isUpperCase() } }
.ifEmpty { listOf("<no uppercase>") }
result.forEach { println(it) }
}
printAllUppercase(listOf("foo", "Bar"))
printAllUppercase(listOf("FOO", "BAR"))
//sampleEnd
}
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.
fun main() {
//sampleStart
val s = " \n"
println(s.ifBlank { "<blank>" })
println(s.ifBlank { null })
//sampleEnd
}
Smaller changes
— Boolean type now has companion.
Tooling
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 di erence 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!
Please, note, that even though kotlinx.serialization now ships with the Kotlin Compiler distribution,
it is still considered to be an experimental feature in Kotlin 1.3.
75
Scripting update
Please note, that scripting is an experimental feature, meaning that no compatibility guarantees on
the API are given.
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.
Scratches support
Kotlin 1.3 introduces support for runnable Kotlin scratch les. Scratch le is a kotlin script le with a .kts
extension which you can run and get evaluation results directly in the editor.
76
What's New in Kotlin 1.2
Table of Contents
— Multiplatform projects
— Other language features
— Standard library
— JVM backend
— JavaScript backend
— A common module contains code that is not speci c to any platform, as well as declarations without
implementation of platform-dependent APIs.
— A platform module contains implementations of platform-dependent declarations in the common
module for a speci c platform, as well as other platform-dependent code.
— A regular module targets a speci c platform and can either be a dependency of platform modules or
depend on platform modules.
When you compile a multiplatform project for a speci c platform, the code for both the common and
platform-speci c parts is generated.
A key feature of the multiplatform project support is the possibility to express dependencies of common
code on platform-speci c parts through expected and actual declarations. An expected declaration
speci es an API (class, interface, annotation, top-level declaration etc.). An actual declaration is either a
platform-dependent implementation of the API or a typealias referring to an existing implementation of the
API in an external library. Here's an example:
In common code:
// expected platform-specific API:
expect fun hello(world: String): String
fun greet() {
// usage of the expected API:
val greeting = hello("multi-platform world")
println(greeting)
}
77
See the documentation for details and steps to build a multiplatform project.
Starting with Kotlin 1.2, array arguments for annotations can be passed with the new array literal syntax
instead of the arrayOf function:
@CacheConfig(cacheNames = ["books", "default"])
public class BookRepositoryImpl {
// ...
}
The lateinit modi er can now be used on top-level properties and local variables. The latter can be
used, for example, when a lambda passed as a constructor argument to one object refers to another object
which has to be de ned later:
class Node<T>(val value: T, val next: () -> Node<T>)
You can now check whether a lateinit var has been initialized using isInitialized on the property
reference:
class Foo {
lateinit var lateinitVar: String
fun initializationLogic() {
//sampleStart
println("isInitialized before assignment: " + this::lateinitVar.isInitialized)
lateinitVar = "value"
println("isInitialized after assignment: " + this::lateinitVar.isInitialized)
//sampleEnd
}
}
78
Inline functions are now allowed to have default values for their inlined functional parameters:
//sampleStart
inline fun <E> Iterable<E>.strings(transform: (E) -> String = { it.toString() }) =
map { transform(it) }
The Kotlin compiler can now use information from type casts in type inference. If you’re calling a generic
method that returns a type parameter T and casting the return value to a speci c type Foo , the compiler
now understands that T for this call needs to be bound to the type Foo .
This is particularly important for Android developers, since the compiler can now correctly analyze generic
findViewById calls in Android API level 26:
val button = findViewById(R.id.button) as Button
When a variable is assigned from a safe call expression and checked for null, the smart cast is now applied
to the safe call receiver as well:
fun countFirst(s: Any): Int {
//sampleStart
val firstChar = (s as? CharSequence)?.firstOrNull()
if (firstChar != null)
return s.count { it == firstChar } // s: Any is smart cast to CharSequence
Also, smart casts in a lambda are now allowed for local variables that are only modi ed before the lambda:
79
fun main(args: Array<String>) {
//sampleStart
val flag = args.size == 0
var x: String? = null
if (flag) x = "Yahoo!"
run {
if (x != null) {
println(x.length) // x is smart cast to String
}
}
//sampleEnd
}
A bound callable reference to a member of this can now be written without explicit receiver, ::foo
instead of this::foo . This also makes callable references more convenient to use in lambdas where you
refer to a member of the outer receiver.
Earlier, Kotlin used assignments made inside a try block for smart casts after the block, which could break
type- and null-safety and lead to runtime failures. This release xes this issue, making the smart casts more
strict, but breaking some code that relied on such smart casts.
To switch to the old smart casts behavior, pass the fallback ag -Xlegacy-smart-cast-after-try as
the compiler argument. It will become deprecated in Kotlin 1.3.
When a data class derived from a type that already had the copy function with the same signature, the
copy implementation generated for the data class used the defaults from the supertype, leading to
counter-intuitive behavior, or failed at runtime if there were no default parameters in the supertype.
Inheritance that leads to a copy con ict has become deprecated with a warning in Kotlin 1.2 and will be an
error in Kotlin 1.3.
Inside enum entries, de ning a nested type that is not an inner class has been deprecated due to issues
in the initialization logic. This causes a warning in Kotlin 1.2 and will become an error in Kotlin 1.3.
For consistency with array literals in annotations, passing a single item for a vararg parameter in the named
form ( foo(items = i) ) has been deprecated. Please use the spread operator with the corresponding
array factory functions:
foo(items = *intArrayOf(1))
There is an optimization that removes redundant arrays creation in such cases, which prevents performance
degradation. The single-argument form produces warnings in Kotlin 1.2 and is to be dropped in Kotlin 1.3.
80
Deprecation: inner classes of generic classes extending Throwable
Inner classes of generic types that inherit from Throwable could violate type-safety in a throw-catch
scenario and thus have been deprecated, with a warning in Kotlin 1.2 and an error in Kotlin 1.3.
Mutating the backing eld of a read-only property by assigning field = ... in the custom getter has
been deprecated, with a warning in Kotlin 1.2 and an error in Kotlin 1.3.
Standard Library
The Kotlin standard library is now fully compatible with the Java 9 module system, which forbids split
packages (multiple jar les declaring classes in the same package). In order to support that, new artifacts
kotlin-stdlib-jdk7 and kotlin-stdlib-jdk8 are introduced, which replace the old kotlin-
stdlib-jre7 and kotlin-stdlib-jre8 .
The declarations in the new artifacts are visible under the same package names from the Kotlin point of
view, but have di erent package names for Java. Therefore, switching to the new artifacts will not require
any changes to your source code.
Another change made to ensure compatibility with the new module system is removing the deprecated
declarations in the kotlin.reflect package from the kotlin-reflect library. If you were using them,
you need to switch to using the declarations in the kotlin.reflect.full package, which is supported
since Kotlin 1.1.
New extensions for Iterable<T> , Sequence<T> , and CharSequence cover such use cases as bu ering
or batch processing ( chunked ), sliding window and computing sliding average ( windowed ) , and
processing pairs of subsequent items ( zipWithNext ):
fun main(args: Array<String>) {
//sampleStart
val items = (1..9).map { it * it }
println("items: $items\n")
81
A set of extension functions was added for manipulating lists: fill , replaceAll and shuffle for
MutableList , and shuffled for read-only List :
fun main(args: Array<String>) {
//sampleStart
val items = (1..5).toMutableList()
items.shuffle()
println("Shuffled items: $items")
items.replaceAll { it * 2 }
println("Items doubled: $items")
items.fill(5)
println("Items filled with 5: $items")
//sampleEnd
}
Satisfying the longstanding request, Kotlin 1.2 adds the kotlin.math API for math operations that is
common for JVM and JS and contains the following:
— Constants: PI and E ;
— Trigonometric: cos , sin , tan and inverse of them: acos , asin , atan , atan2 ;
— Hyperbolic: cosh , sinh , tanh and their inverse: acosh , asinh , atanh
— Rounding:
— ceil , floor , truncate , round (half to even) functions;
— Binary representation:
— ulp extension property;
The same set of functions (but without constants) is also available for Float arguments.
Kotlin 1.2 introduces a set of functions for operating with BigInteger and BigDecimal and creating
them from other numeric types. These are:
82
— toBigInteger for Int and Long ;
New functions were added for converting Double and Float to and from their bit representations:
— toBits and toRawBits returning Long for Double and Int for Float ;
— Double.fromBits and Float.fromBits for creating oating point numbers from the bit
representation.
The kotlin.text.Regex class has become Serializable and can now be used in serializable
hierarchies.
JVM Backend
Ever since version 1.0, Kotlin supported expressions with complex control ow, such as try-catch
expressions and inline function calls. Such code is valid according to the Java Virtual Machine speci cation.
Unfortunately, some bytecode processing tools do not handle such code quite well when such expressions
are present in the arguments of constructor calls.
To mitigate this problem for the users of such bytecode processing tools, we’ve added a command-line
option ( -Xnormalize-constructor-calls=MODE ) that tells the compiler to generate more Java-like
bytecode for such constructs. Here MODE is one of:
— disable (default) – generate bytecode in the same way as in Kotlin 1.0 and 1.1;
— enable – generate Java-like bytecode for constructor calls. This can change the order in which the
classes are loaded and initialized;
— preserve-class-initialization – generate Java-like bytecode for constructor calls, ensuring that
the class initialization order is preserved. This can a ect overall performance of your application; use it
only if you have some complex state shared between multiple classes and updated on class initialization.
83
The “manual” workaround is to store the values of sub-expressions with control ow in variables, instead of
evaluating them directly inside the call arguments. It’s similar to -Xnormalize-constructor-
calls=enable .
Before Kotlin 1.2, interface members overriding Java-default methods while targeting JVM 1.6 produced a
warning on super calls: Super calls to Java default methods are deprecated in JVM target
1.6. Recompile with '-jvm-target 1.8' . In Kotlin 1.2, there's an error instead, thus requiring any
such code to be compiled with JVM target 1.8.
Calling x.equals(null) on a platform type that is mapped to a Java primitive ( Int! , Boolean! ,
Short !, Long! , Float! , Double! , Char! ) incorrectly returned true when x was null. Starting with
Kotlin 1.2, calling x.equals(...) on a null value of a platform type throws an NPE (but x == ... does
not).
Breaking change: x for platform null escaping through an inlined extension receiver
Inline extension functions that were called on a null value of a platform type did not check the receiver for
null and would thus allow null to escape into the other code. Kotlin 1.2 forces this check at the call sites,
throwing an exception if the receiver is null.
To switch to the old behavior, pass the fallback ag -Xno-receiver-assertions to the compiler.
JavaScript Backend
The JS typed arrays support that translates Kotlin primitive arrays, such as IntArray , DoubleArray , into
JavaScript typed arrays, that was previously an opt-in feature, has been enabled by default.
Tools
Warnings as errors
The compiler now provides an option to treat all warnings as errors. Use -Werror on the command line,
or the following Gradle snippet:
compileKotlin {
kotlinOptions.allWarningsAsErrors = true
}
84
What's New in Kotlin 1.1
Table of Contents
— Coroutines
— Other language features
— Standard library
— JVM backend
— JavaScript backend
JavaScript
Starting with Kotlin 1.1, the JavaScript target is no longer considered experimental. All language features are
supported, and there are many new tools for integration with the frontend development environment. See
below for a more detailed list of changes.
Coroutines (experimental)
The key new feature in Kotlin 1.1 is coroutines, bringing the support of async / await , yield and similar
programming patterns. The key feature of Kotlin's design is that the implementation of coroutine execution
is part of the libraries, not the language, so you aren't bound to any speci c programming paradigm or
concurrency library.
A coroutine is e ectively a light-weight thread that can be suspended and resumed later. Coroutines are
supported through suspending functions: a call to such a function can potentially suspend a coroutine, and to
start a new coroutine we usually use an anonymous suspending functions (i.e. suspending lambdas).
Here, async { ... } starts a coroutine and, when we use await() , the execution of the coroutine is
suspended while the operation being awaited is executed, and is resumed (possibly on a di erent thread)
when the operation being awaited completes.
The standard library uses coroutines to support lazily generated sequences with yield and yieldAll
functions. In such a sequence, the block of code that returns sequence elements is suspended after each
element has been retrieved, and resumed when the next element is requested. Here's an example:
85
import kotlin.coroutines.experimental.*
Run the code above to see the result. Feel free to edit it and run again!
For more information, please refer to the coroutine documentation and tutorial.
Note that coroutines are currently considered an experimental feature, meaning that the Kotlin team is
not committing to supporting the backwards compatibility of this feature after the nal 1.1 release.
Type aliases
A type alias allows you to de ne an alternative name for an existing type. This is most useful for generic
types such as collections, as well as for function types. Here is an example:
//sampleStart
typealias OscarWinners = Map<String, String>
// Note that the type names (initial and the type alias) are interchangeable:
fun checkLaLaLandIsTheBestMovie(oscarWinners: Map<String, String>) =
oscarWinners["Best picture"] == "La La Land"
//sampleEnd
86
You can now use the :: operator to get a member reference pointing to a method or property of a speci c
object instance. Previously this could only be expressed with a lambda. Here's an example:
//sampleStart
val numberRegex = "\\d+".toRegex()
val numbers = listOf("abc", "123", "456").filter(numberRegex::matches)
//sampleEnd
Kotlin 1.1 removes some of the restrictions on sealed and data classes that were present in Kotlin 1.0. Now
you can de ne subclasses of a top-level sealed class on the top level in the same le, and not just as nested
classes of the sealed class. Data classes can now extend other classes. This can be used to de ne a hierarchy
of expression classes nicely and cleanly:
//sampleStart
sealed class Expr
Read the documentation or sealed class and data class KEEPs for more detail.
Destructuring in lambdas
You can now use the destructuring declaration syntax to unpack the arguments passed to a lambda. Here's
an example:
fun main(args: Array<String>) {
//sampleStart
val map = mapOf(1 to "one", 2 to "two")
// before
println(map.mapValues { entry ->
val (key, value) = entry
"$key -> $value!"
})
// now
println(map.mapValues { (key, value) -> "$key -> $value!" })
//sampleEnd
}
87
Underscores for unused parameters
For a lambda with multiple parameters, you can use the _ character to replace the names of the
parameters you don't use:
fun main(args: Array<String>) {
val map = mapOf(1 to "one", 2 to "two")
//sampleStart
map.forEach { _, value -> println("$value!") }
//sampleEnd
}
Just as in Java 8, Kotlin now allows to use underscores in numeric literals to separate groups of digits:
//sampleStart
val oneMillion = 1_000_000
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
//sampleEnd
For properties with the getter de ned as an expression body, the property type can now be omitted:
//sampleStart
data class Person(val name: String, val age: Int) {
val isAdult get() = age >= 20 // Property type inferred to be 'Boolean'
}
//sampleEnd
fun main(args: Array<String>) {
val akari = Person("Akari", 26)
println("$akari.isAdult = ${akari.isAdult}")
}
88
You can now mark property accessors with the inline modi er if the properties don't have a backing
eld. Such accessors are compiled in the same way as inline functions.
//sampleStart
public val <T> List<T>.lastIndex: Int
inline get() = this.size - 1
//sampleEnd
You can also mark the entire property as inline - then the modi er is applied to both accessors.
You can now use the delegated property syntax with local variables. One possible use is de ning a lazily
evaluated local variable:
import java.util.Random
For delegated properties, it is now possible to intercept delegate to property binding using the
provideDelegate operator. For example, if we want to check the property name before binding, we can
write something like this:
89
class ResourceLoader<T>(id: ResourceID<T>) {
operator fun provideDelegate(thisRef: MyUI, prop: KProperty<*>): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, prop.name)
... // property creation
}
class MyUI {
val image by bindResource(ResourceID.image_id)
val text by bindResource(ResourceID.text_id)
}
The provideDelegate method will be called for each property during the creation of a MyUI instance,
and it can perform the necessary validation right away.
The @DslMarker annotation allows to restrict the use of receivers from outer scopes in a DSL context.
Consider the canonical HTML builder example:
table {
tr {
td { + "Text" }
}
}
In Kotlin 1.0, code in the lambda passed to td has access to three implicit receivers: the one passed to
table , to tr and to td . This allows you to call methods that make no sense in the context - for example
to call tr inside td and thus to put a <tr> tag in a <td> .
In Kotlin 1.1, you can restrict that, so that only methods de ned on the implicit receiver of td will be
available inside the lambda passed to td . You do that by de ning your annotation marked with the
@DslMarker meta-annotation and applying it to the base class of the tag classes.
rem operator
90
The mod operator is now deprecated, and rem is used instead. See this issue for motivation.
Standard library
There is a bunch of new extensions on the String class to convert it to a number without throwing an
exception on invalid number: String.toIntOrNull(): Int? , String.toDoubleOrNull():
Double? etc.
val port = System.getenv("PORT")?.toIntOrNull() ?: 80
onEach()
onEach is a small, but useful extension function for collections and sequences, which allows to perform
some action, possibly with side-e ects, on each element of the collection/sequence in a chain of operations.
On iterables it behaves like forEach but also returns the iterable instance further. And on sequences it
returns a wrapping sequence, which applies the given action lazily as the elements are being iterated.
inputDir.walk()
.filter { it.isFile && it.name.endsWith(".txt") }
.onEach { println("Moving $it to $outputDir") }
.forEach { moveFile(it, File(outputDir, it.toRelativeString(inputDir))) }
also is like apply : it takes the receiver, does some action on it, and returns that receiver. The di erence
is that in the block inside apply the receiver is available as this , while in the block inside also it's
available as it (and you can give it another name if you want). This comes handy when you do not want to
shadow this from the outer scope:
class Block {
lateinit var content: String
}
//sampleStart
fun Block.copy() = Block().also {
it.content = this.content
}
//sampleEnd
91
takeIf is like filter for a single value. It checks whether the receiver meets the predicate, and returns
the receiver, if it does or null if it doesn't. Combined with an elvis-operator and early returns it allows to
write constructs like:
val outDirFile = File(outputDir.path).takeIf { it.exists() } ?: return false
// do something with existing outDirFile
//sampleStart
val index = input.indexOf(keyword).takeIf { it >= 0 } ?: error("keyword not found")
// do something with index of keyword in input string, given that it's found
//sampleEnd
takeUnless is the same as takeIf , but it takes the inverted predicate. It returns the receiver when it
doesn't meet the predicate and null otherwise. So one of the examples above could be rewritten with
takeUnless as following:
val index = input.indexOf(keyword).takeUnless { it < 0 } ?: error("keyword not found")
It is also convenient to use when you have a callable reference instead of the lambda:
private fun testTakeUnless(string: String) {
//sampleStart
val result = string.takeUnless(String::isEmpty)
//sampleEnd
groupingBy()
This API can be used to group a collection by key and fold each group simultaneously. For example, it can be
used to count the number of words starting with each letter:
fun main(args: Array<String>) {
val words = "one two three four five six seven eight nine ten".split(' ')
//sampleStart
val frequencies = words.groupingBy { it.first() }.eachCount()
//sampleEnd
println("Counting first letters: $frequencies.")
// The alternative way that uses 'groupBy' and 'mapValues' creates an intermediate map,
// while 'groupingBy' way counts on the fly.
val groupBy = words.groupBy { it.first() }.mapValues { (_, list) -> list.size }
println("Comparing the result with using 'groupBy': ${groupBy == frequencies}.")
}
92
class ImmutablePropertyBag(map: Map<String, Any>) {
private val mapCopy = map.toMap()
}
Map.minus(key)
The operator plus provides a way to add key-value pair(s) to a read-only map producing a new map,
however there was not a simple way to do the opposite: to remove a key from the map you have to resort
to less straightforward ways to like Map.filter() or Map.filterKeys() . Now the operator minus
lls this gap. There are 4 overloads available: for removing a single key, a collection of keys, a sequence of
keys and an array of keys.
fun main(args: Array<String>) {
//sampleStart
val map = mapOf("key" to 42)
val emptyMap = map - "key"
//sampleEnd
println("map: $map")
println("emptyMap: $emptyMap")
}
These functions can be used to nd the lowest and greatest of two or three given values, where values are
primitive numbers or Comparable objects. There is also an overload of each function that take an
additional Comparator instance, if you want to compare objects that are not comparable themselves.
fun main(args: Array<String>) {
//sampleStart
val list1 = listOf("a", "b")
val list2 = listOf("x", "y", "z")
val minSize = minOf(list1.size, list2.size)
val longestList = maxOf(list1, list2, compareBy { it.size })
//sampleEnd
println("minSize = $minSize")
println("longestList = $longestList")
}
Similar to the Array constructor, there are now functions that create List and MutableList instances
and initialize each element by calling a lambda:
fun main(args: Array<String>) {
//sampleStart
val squares = List(10) { index -> index * index }
val mutable = MutableList(10) { 0 }
//sampleEnd
println("squares: $squares")
println("mutable: $mutable")
}
Map.getValue()
93
This extension on Map returns an existing value corresponding to the given key or throws an exception,
mentioning which key was not found. If the map was produced with withDefault , this function will return
the default value instead of throwing an exception.
fun main(args: Array<String>) {
//sampleStart
val map = mapOf("key" to 42)
// returns non-nullable Int value 42
val value: Int = map.getValue("key")
println("value is $value")
println("value2 is $value2")
}
Abstract collections
These abstract classes can be used as base classes when implementing Kotlin collection classes. For
implementing read-only collections there are AbstractCollection , AbstractList , AbstractSet
and AbstractMap , and for mutable collections there are AbstractMutableCollection ,
AbstractMutableList , AbstractMutableSet and AbstractMutableMap . On JVM these abstract
mutable collections inherit most of their functionality from JDK's abstract collections.
The standard library now provides a set of functions for element-by-element operations on arrays:
comparison ( contentEquals and contentDeepEquals ), hash code calculation ( contentHashCode
and contentDeepHashCode ), and conversion to a string ( contentToString and
contentDeepToString ). They're supported both for the JVM (where they act as aliases for the
corresponding functions in java.util.Arrays ) and for JS (where the implementation is provided in the
Kotlin standard library).
fun main(args: Array<String>) {
//sampleStart
val array = arrayOf("a", "b", "c")
println(array.toString()) // JVM implementation: type-and-hash gibberish
println(array.contentToString()) // nicely formatted as list
//sampleEnd
}
JVM Backend
Kotlin has now the option of generating Java 8 bytecode ( -jvm-target 1.8 command line option or the
corresponding options in Ant/Maven/Gradle). For now this doesn't change the semantics of the bytecode (in
particular, default methods in interfaces and lambdas are generated exactly as in Kotlin 1.0), but we plan to
make further use of this later.
94
Java 8 standard library support
There are now separate versions of the standard library supporting the new JDK APIs added in Java 7 and 8.
If you need access to the new APIs, use kotlin-stdlib-jre7 and kotlin-stdlib-jre8 maven
artifacts instead of the standard kotlin-stdlib . These artifacts are tiny extensions on top of kotlin-
stdlib and they bring it to your project as a transitive dependency.
Kotlin now supports storing parameter names in the bytecode. This can be enabled using the -java-
parameters command line option.
Constant inlining
The compiler now inlines values of const val properties into the locations where they are used.
The box classes used for capturing mutable closure variables in lambdas no longer have volatile elds. This
change improves performance, but can lead to new race conditions in some rare usage scenarios. If you're
a ected by this, you need to provide your own synchronization for accessing the variables.
javax.script support
Kotlin now integrates with the javax.script API (JSR-223). The API allows to evaluate snippets of code at
runtime:
val engine = ScriptEngineManager().getEngineByExtension("kts")!!
engine.eval("val x = 3")
println(engine.eval("x + 2")) // Prints out 5
kotlin.re ect.full
To prepare for Java 9 support, the extension functions and properties in the kotlin-reflect.jar library
have been moved to the package kotlin.reflect.full . The names in the old package
( kotlin.reflect ) are deprecated and will be removed in Kotlin 1.2. Note that the core re ection
interfaces (such as KClass ) are part of the Kotlin standard library, not kotlin-reflect , and are not
a ected by the move.
JavaScript Backend
A much larger part of the Kotlin standard library can now be used from code compiled to JavaScript. In
particular, key classes such as collections ( ArrayList , HashMap etc.), exceptions
( IllegalArgumentException etc.) and a few others ( StringBuilder , Comparator ) are now de ned
under the kotlin package. On the JVM, the names are type aliases for the corresponding JDK classes, and
on the JS, the classes are implemented in the Kotlin standard library.
95
Better code generation
JavaScript backend now generates more statically checkable code, which is friendlier to JS code processing
tools, like mini ers, optimisers, linters, etc.
If you need to access a class implemented in JavaScript from Kotlin in a typesafe way, you can write a Kotlin
declaration using the external modi er. (In Kotlin 1.0, the @native annotation was used instead.)
Unlike the JVM target, the JS one permits to use external modi er with classes and properties. For example,
here's how you can declare the DOM Node class:
external class Node {
val firstChild: Node
// etc
}
You can now describe declarations which should be imported from JavaScript modules more precisely. If
you add the @JsModule("<module-name>") annotation on an external declaration it will be properly
imported to a module system (either CommonJS or AMD) during the compilation. For example, with
CommonJS the declaration will be imported via require(...) function. Additionally, if you want to import
a declaration either as a module or as a global JavaScript object, you can use the @JsNonModule
annotation.
For example, here's how you can import JQuery into a Kotlin module:
external interface JQuery {
fun toggle(duration: Int = definedExternally): JQuery
fun click(handler: (Event) -> Unit): JQuery
}
@JsModule("jquery")
@JsNonModule
@JsName("$")
external fun jquery(selector: String): JQuery
In this case, JQuery will be imported as a module named jquery . Alternatively, it can be used as a $-object,
depending on what module system Kotlin compiler is con gured to use.
96
Releases and Roadmap
Kotlin Releases
We ship di erent types of releases:
For example, for the feature release 1.3 we had several incremental releases including 1.3.10, 1.3.20, and
1.3.70. For 1.3.70, we had 2 bug x releases – 1.3.71 and 1.3.72.
For each incremental and feature release, we also ship several preview (EAP) versions for you to try new
features before they are released. See Early Access Preview for details.
If you have projects created with earlier Kotlin versions, change the Kotlin version in your projects and
update kotlinx libraries if necessary – check the recommended versions.
If you are migrating to the new feature release, Kotlin plugin's migration tools will help you with the
migration.
Release details
The following table lists details of latest Kotlin releases.
97
1.4.30
An incremental release with various — kotlinx.serialization version: 1.1.0-RC
Released:
improvements such as: — kotlinx.coroutines version: 1.4.2
February
3, 2021 — New JVM backend, now in Beta — kotlinx.atomicfu version: 0.15.1
— Preview of new language features — ktor version: 1.5.1
Release on
GitHub — Improved Kotlin/Native performance — kotlinx.html version: 0.7.2
— Standard library API improvements — kotlinx-nodejs version: 0.0.7
1.4.21
A bug x release for Kotlin 1.4.20 — kotlinx.serialization version: 1.0.1
Released:
December Learn more about Kotlin 1.4.20. — kotlinx.coroutines version: 1.4.1
1.4.20
An incremental release with various — kotlinx.serialization version: 1.0.1
Released:
improvements such as: — kotlinx.coroutines version: 1.4.1
November
23, 2020 — Supporting new JVM features, like — kotlinx.atomicfu version: 0.14.4
string concatenation via — ktor version: 1.4.1
Release on
invokedynamic
GitHub — kotlinx.html version: 0.7.2
— Improved performance and
— kotlinx-nodejs version: 0.0.6
exception handling for KMM projects
The versions of libraries from kotlin-
— Extensions for JDK Path:
wrappers (such as kotlin-react ) can be
Path(“dir”) / “file.txt”
found in the corresponding repository.
Learn more in:
98
1.4.10
A bug x release for Kotlin 1.4.0. — kotlinx.serialization version: 1.0.0-RC
Released:
September Learn more about Kotlin 1.4.0. — kotlinx.coroutines version: 1.3.9
1.4.0
A feature release with many features — kotlinx.serialization version: 1.0.0-RC
Released:
and improvements that mostly focus on — kotlinx.coroutines version: 1.3.9
August 17,
quality and performance.
2020 — kotlinx.atomicfu version: 0.14.4
Learn more in: — ktor version: 1.4.0
Release on
GitHub — Release blog post — kotlinx.html version: 0.7.2
— What's new in Kotlin 1.4.0 — kotlinx-nodejs version: 0.0.6
— Compatibility Guide The versions of libraries from kotlin-
— Migrating to Kotlin 1.4.0 wrappers (such as kotlin-react ) can be
found in the corresponding repository.
1.3.72
A bug x release for Kotlin 1.3.70. — kotlinx.serialization version: 0.20.0
Released:
April 15, Learn more about Kotlin 1.3.70. — kotlinx.coroutines version: 1.3.8
On the JVM, you usually can use library versions other than the recommended ones.
99
Kotlin Roadmap
Last modified on January 2021
Time frame 6 months until June 2021
Next update April 2021
Welcome to the Kotlin roadmap! Get a sneak peek into the priorities of the Kotlin Team.
— Key priorities
— Kotlin roadmap by subsystem
— What's changed since October 2020
Key priorities
The goal of this roadmap is to give you a big picture. Here’s a list of our key priorities – the areas we are
investing the most e ort into:
If you have any questions or feedback about the roadmap or the items on it, feel free to post them to
YouTrack tickets or in the #kotlin-roadmap channel of Kotlin Slack (request an invite).
YouTrack board
Roadmap details
Subsystem In focus now Postponed for later
Language
— Support JVM records
— Support JVM sealed classes
— Release inline classes as Stable, secure
Valhalla compatibility
— Prototype multiple receivers
100
Compiler core
— Support incremental compilation for the — Stable Compiler Plugin API
new compiler — Scripting improvements
— Work on services for the new compiler
to interact with IDE
— Maintain the current compiler (bug- xing
only)
Kotlin/JVM
— Make the new JVM IR backend Stable — Maintain the old JVM backend by xing
critical bugs
Kotlin/JS
— Make the new JS IR backend Stable — ES6 support
— JS IR BE: Prototype lazy initialization for — Improve Dukat support
top-level properties like in JVM
— JS IR BE: Add an ability to generate
separate JS les for each module
— Maintain the old JS backend by xing
critical bugs
Kotlin/Native
— Provide binary compatibility between — Support Mac Catalyst
incremental releases — Development with Kotlin/Native on
— Improve compilation time Apple Silicon without Rosetta 2
— Prototype a new garbage collector — Direct interoperability with Swift
— Improve exporting Kotlin code to — Interoperability with C++
Objective-C — Support Alpine Linux
— Support producing binaries that run on
Apple Silicon without Rosetta 2
Kotlin
Multiplatform — Improve frontend and IDE import — Improve dependency management for
stability for Multiplatform projects iOS
— Introduce a complex KMM application — Improve Gradle & Compiler error
sample messages
— Improve Kotlin/Native debugging — Sharing code between JVM and
experience Android
— Improve UX of using Native libraries in — KMM plugin: support for IntelliJ IDEA
Kotlin
101
IDE
— Improve IDE performance — Advanced tooling that users have in
— Improve debugging experience Java but is missing in Kotlin.
Build tools
— Improve incremental compilation — Improvements in Kotlin Maven
performance in Gradle support
— Support the Gradle con guration cache
— Decrease time for opening Gradle
projects
Libraries
— Support java.nio.Path extension in — Improve usability of multi-threaded
the standard library coroutines library for Kotlin/Native
— Make multiplatform kotlin.text API — kotlinx-cli
locale-agnostic by default — binary-compatibility-
— Improve kotlinx-datetime library validator
— Improve kotlinx-serialization — kotlinx-io
(release v1.1) — Any new multiplatform libraries
— Improve kotlinx-serialization
(release v1.2)
— Improve kotlinx-coroutines (release
v1.5)
— Implement unambiguous API for Char
conversion
— Implement multiplatform API for
characters
Website
— Revamp Kotlin documentation
— Make the Kotlin website mobile friendly
— Design a new Kotlin visual style
— Close try.kotlinlang.org
Ktor
Ktor roadmap
102
— This roadmap is not an exhaustive list of all things the team is working on, only the biggest
projects.
— There’s no commitment to delivering speci c features or xes in speci c versions.
— It lists some things that are postponed and will NOT get the team’s attention in the nearest
future.
— We will adjust our priorities as we go and update the roadmap approximately every three
months.
Completed items
We've completed the following items from the roadmap that we published in October 2020:
— Compiler core: Bootstrap the new compiler (make the new Kotlin compiler compile itself)
— Kotlin/Native: Runtime performance: improve object allocation times
— Multiplatform: KMM plugin: Fix major bugs
— Multiplatform: KMM plugin: Run common tests on Android devices (partially)
— Libraries: Improve kotlinx-coroutines(release v1.4)
Postponed items
We've decided to postpone the following items from the previous roadmap:
New items
103
— IDE: Improve debugging experience
— Libraries: Improve kotlinx-serialization (release v1.2)
— Libraries: Improve kotlinx-coroutines (release v1.5)
— Libraries: Implement unambiguous API for Char conversion
— Libraries: Implement multiplatform API for characters
We've also added a new subsystem, Website, to share our roadmap for kotlinlang.org:
Items in progress
All other previously identi ed roadmap items are in progress. You can check their YouTrack tickets for
updates.
104
Getting Started
Basic Syntax
import kotlin.text.*
// ...
It is not required to match directories and packages: source les can be placed arbitrarily in the le system.
See Packages.
Functions
Function having two Int parameters with Int return type:
//sampleStart
fun sum(a: Int, b: Int): Int {
return a + b
}
//sampleEnd
fun main() {
print("sum of 3 and 5 is ")
println(sum(3, 5))
}
fun main() {
println("sum of 19 and 23 is ${sum(19, 23)}")
}
105
//sampleStart
fun printSum(a: Int, b: Int): Unit {
println("sum of $a and $b is ${a + b}")
}
//sampleEnd
fun main() {
printSum(-1, 8)
}
fun main() {
printSum(-1, 8)
}
See Functions.
Variables
Read-only local variables are de ned using the keyword val . They can be assigned a value only once.
fun main() {
//sampleStart
val a: Int = 1 // immediate assignment
val b = 2 // `Int` type is inferred
val c: Int // Type required when no initializer is provided
c = 3 // deferred assignment
//sampleEnd
println("a = $a, b = $b, c = $c")
}
Top-level variables:
//sampleStart
val PI = 3.14
var x = 0
fun incrementX() {
x += 1
}
//sampleEnd
fun main() {
println("x = $x; PI = $PI")
incrementX()
println("incrementX()")
println("x = $x; PI = $PI")
}
106
Comments
Just like most modern languages, Kotlin supports single-line (or end-of-line) and multi-line (block) comments.
// This is an end-of-line comment
See Documenting Kotlin Code for information on the documentation comment syntax.
String templates
fun main() {
//sampleStart
var a = 1
// simple name in template:
val s1 = "a is $a"
a = 2
// arbitrary expression in template:
val s2 = "${s1.replace("is", "was")}, but now is $a"
//sampleEnd
println(s2)
}
Conditional expressions
//sampleStart
fun maxOf(a: Int, b: Int): Int {
if (a > b) {
return a
} else {
return b
}
}
//sampleEnd
fun main() {
println("max of 0 and 42 is ${maxOf(0, 42)}")
}
fun main() {
println("max of 0 and 42 is ${maxOf(0, 42)}")
}
See if-expressions.
107
Return null if str does not hold an integer:
fun parseInt(str: String): Int? {
// ...
}
//sampleStart
fun printProduct(arg1: String, arg2: String) {
val x = parseInt(arg1)
val y = parseInt(arg2)
fun main() {
printProduct("6", "7")
printProduct("a", "7")
printProduct("a", "b")
}
or
fun parseInt(str: String): Int? {
return str.toIntOrNull()
}
//sampleStart
// ...
if (x == null) {
println("Wrong number format in arg1: '$arg1'")
return
}
if (y == null) {
println("Wrong number format in arg2: '$arg2'")
return
}
fun main() {
printProduct("6", "7")
printProduct("a", "7")
printProduct("99", "b")
}
108
See Null-safety.
fun main() {
fun printLength(obj: Any) {
println("'$obj' string length is ${getStringLength(obj) ?: "... err, not a string"} ")
}
printLength("Incomprehensibilities")
printLength(1000)
printLength(listOf(Any()))
}
or
//sampleStart
fun getStringLength(obj: Any): Int? {
if (obj !is String) return null
fun main() {
fun printLength(obj: Any) {
println("'$obj' string length is ${getStringLength(obj) ?: "... err, not a string"} ")
}
printLength("Incomprehensibilities")
printLength(1000)
printLength(listOf(Any()))
}
or even
109
//sampleStart
fun getStringLength(obj: Any): Int? {
// `obj` is automatically cast to `String` on the right-hand side of `&&`
if (obj is String && obj.length > 0) {
return obj.length
}
return null
}
//sampleEnd
fun main() {
fun printLength(obj: Any) {
println("'$obj' string length is ${getStringLength(obj) ?: "... err, is empty or not a string
at all"} ")
}
printLength("Incomprehensibilities")
printLength("")
printLength(1000)
}
for loop
fun main() {
//sampleStart
val items = listOf("apple", "banana", "kiwifruit")
for (item in items) {
println(item)
}
//sampleEnd
}
or
fun main() {
//sampleStart
val items = listOf("apple", "banana", "kiwifruit")
for (index in items.indices) {
println("item at $index is ${items[index]}")
}
//sampleEnd
}
while loop
fun main() {
//sampleStart
val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
println("item at $index is ${items[index]}")
index++
}
//sampleEnd
}
when expression
110
//sampleStart
fun describe(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long"
!is String -> "Not a string"
else -> "Unknown"
}
//sampleEnd
fun main() {
println(describe(1))
println(describe("Hello"))
println(describe(1000L))
println(describe(2))
println(describe("other"))
}
Ranges
Check if a number is within a range using in operator:
fun main() {
//sampleStart
val x = 10
val y = 9
if (x in 1..y+1) {
println("fits in range")
}
//sampleEnd
}
or over a progression:
111
fun main() {
//sampleStart
for (x in 1..10 step 2) {
print(x)
}
println()
for (x in 9 downTo 0 step 3) {
print(x)
}
//sampleEnd
}
See Ranges.
Collections
Iterating over a collection:
fun main() {
val items = listOf("apple", "banana", "kiwifruit")
//sampleStart
for (item in items) {
println(item)
}
//sampleEnd
}
112
fun main() {
//sampleStart
val rectangle = Rectangle(5.0, 2.0)
val triangle = Triangle(3.0, 4.0, 5.0)
//sampleEnd
println("Area of rectangle is ${rectangle.calculateArea()}, its perimeter is
${rectangle.perimeter}")
println("Area of triangle is ${triangle.calculateArea()}, its perimeter is
${triangle.perimeter}")
}
interface RectangleProperties {
val isSquare: Boolean
}
class Rectangle(
var height: Double,
var length: Double
) : Shape(listOf(height, length, height, length)), RectangleProperties {
override val isSquare: Boolean get() = length == height
override fun calculateArea(): Double = height * length
}
class Triangle(
var sideA: Double,
var sideB: Double,
var sideC: Double
) : Shape(listOf(sideA, sideB, sideC)) {
override fun calculateArea(): Double {
val s = perimeter / 2
return Math.sqrt(s * (s - sideA) * (s - sideB) * (s - sideC))
}
}
113
Idioms
A collection of random and frequently used idioms in Kotlin. If you have a favorite idiom, contribute it by
sending a pull request.
— equals()
— hashCode()
— toString()
— copy()
Filtering a list
val positives = list.filter { x -> x > 0 }
String Interpolation
println("Name $name")
Instance Checks
when (x) {
is Foo -> ...
is Bar -> ...
else -> ...
}
114
Using ranges
for (i in 1..100) { ... } // closed range: includes 100
for (i in 1 until 100) { ... } // half-open range: does not include 100
for (x in 2..10 step 2) { ... }
for (x in 10 downTo 1) { ... }
if (x in 1..10) { ... }
Read-only list
val list = listOf("a", "b", "c")
Read-only map
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
Accessing a map
println(map["key"])
map["key"] = value
Lazy property
val p: String by lazy {
// compute the string
}
Extension Functions
fun String.spaceToCamelCase() { ... }
Creating a singleton
object Resource {
val name = "Name"
}
fun main() {
val myObject = object : MyAbstractClass() {
override fun doSomething() {
// ...
}
115
val files = File("Test").listFiles()
println(files?.size)
println(files?.size ?: "empty")
value?.let {
... // execute this block if not null
}
'try/catch' expression
fun test() {
val result = try {
count()
} catch (e: ArithmeticException) {
throw IllegalStateException(e)
}
'if' expression
116
fun foo(param: Int) {
val result = if (param == 1) {
"one"
} else if (param == 2) {
"two"
} else {
"three"
}
}
Single-expression functions
fun theAnswer() = 42
This is equivalent to
fun theAnswer(): Int {
return 42
}
This can be e ectively combined with other idioms, leading to shorter code. E.g. with the when-expression:
fun transform(color: String): Int = when (color) {
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("Invalid color param value")
}
This is useful for con guring properties that aren't present in the object constructor.
117
Java 7's try with resources
val stream = Files.newInputStream(Paths.get("/some/file.txt"))
stream.buffered().reader().use { reader ->
println(reader.readText())
}
Convenient form for a generic function that requires the generic type information
// public final class Gson {
// ...
// public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException {
// ...
Kotlin's standard library has a TODO() function that will always throw a NotImplementedError . Its
return type is Nothing so it can be used regardless of expected type. There's also an overload that accepts
a reason parameter:
IntelliJ IDEA's kotlin plugin understands the semantics of TODO() and automatically adds a code pointer in
the TODO tool window.
118
Coding Conventions
This page contains the current coding style for the Kotlin language.
To con gure the IntelliJ formatter according to this style guide, please install Kotlin plugin version 1.2.20 or
newer, go to Settings | Editor | Code Style | Kotlin, click Set from… link in the upper right corner, and
select Kotlin style guide from the menu.
To verify that your code is formatted according to the style guide, go to Settings | Editor | Inspections and
enable the Kotlin | Style issues | File is not formatted according to project settings inspection.
Additional inspections that verify other issues described in the style guide (such as naming conventions) are
enabled by default.
Directory structure
In pure Kotlin projects, the recommended directory structure follows the package structure with the
common root package omitted. For example, if all the code in the project is in the org.example.kotlin
package and its subpackages, les with the org.example.kotlin package should be placed directly
under the source root, and les in org.example.kotlin.network.socket should be in the
network/socket subdirectory of the source root.
On the JVM: In projects where Kotlin is used together with Java, Kotlin source les should reside in the
same source root as the Java source les, and follow the same directory structure: each le should be
stored in the directory corresponding to each package statement.
Source le names
If a Kotlin le contains a single class (potentially with related top-level declarations), its name should be the
same as the name of the class, with the .kt extension appended. If a le contains multiple classes, or only
top-level declarations, choose a name describing what the le contains, and name the le accordingly. Use
upper camel case with an uppercase rst letter (also known as Pascal case), for example,
ProcessDeclarations.kt .
The name of the le should describe what the code in the le does. Therefore, you should avoid using
meaningless words such as "Util" in le names.
119
Source le organization
Placing multiple declarations (classes, top-level functions or properties) in the same Kotlin source le is
encouraged as long as these declarations are closely related to each other semantically and the le size
remains reasonable (not exceeding a few hundred lines).
In particular, when de ning extension functions for a class which are relevant for all clients of this class, put
them in the same le where the class itself is de ned. When de ning extension functions that make sense
only for a speci c client, put them next to the code of that client. Do not create les just to hold "all
extensions of Foo".
Class layout
Do not sort the method declarations alphabetically or by visibility, and do not separate regular methods
from extension methods. Instead, put related stu together, so that someone reading the class from top to
bottom can follow the logic of what's happening. Choose an order (either higher-level stu rst, or vice
versa) and stick to it.
Put nested classes next to the code that uses those classes. If the classes are intended to be used externally
and aren't referenced inside the class, put them in the end, after the companion object.
When implementing an interface, keep the implementing members in the same order as members of the
interface (if necessary, interspersed with additional private methods used for the implementation)
Overload layout
Naming rules
Package and class naming rules in Kotlin are quite simple:
— Names of packages are always lower case and do not use underscores ( org.example.project ). Using
multi-word names is generally discouraged, but if you do need to use multiple words, you can either
simply concatenate them together or use camel case ( org.example.myProject ).
— Names of classes and objects start with an upper case letter and use camel case:
open class DeclarationProcessor { /*...*/ }
Function names
120
Names of functions, properties and local variables start with a lower case letter and use camel case and no
underscores:
fun processDeclarations() { /*...*/ }
var declarationCount = 1
Exception: factory functions used to create instances of classes can have the same name as the abstract
return type:
interface Foo { /*...*/ }
In tests (and only in tests), it's acceptable to use method names with spaces enclosed in backticks. (Note that
such method names are currently not supported by the Android runtime.) Underscores in method names
are also allowed in test code.
class MyTestCase {
@Test fun `ensure everything works`() { /*...*/ }
Property names
Names of constants (properties marked with const , or top-level or object val properties with no custom
get function that hold deeply immutable data) should use uppercase underscore-separated names
(screaming snake case) names:
const val MAX_COUNT = 8
val USER_NAME_FIELD = "UserName"
Names of top-level or object properties which hold objects with behavior or mutable data should use camel
case names:
val mutableCollection: MutableSet<String> = HashSet()
Names of properties holding references to singleton objects can use the same naming style as object
declarations:
val PersonComparator: Comparator<Person> = /*...*/
For enum constants, it's OK to use either uppercase underscore-separated names (screaming snake case)
( enum class Color { RED, GREEN } ) or upper camel case names, depending on the usage.
If a class has two properties which are conceptually the same but one is part of a public API and another is
an implementation detail, use an underscore as the pre x for the name of the private property:
class C {
private val _elementList = mutableListOf<Element>()
121
Choosing good names
The name of a class is usually a noun or a noun phrase explaining what the class is: List , PersonReader .
The name of a method is usually a verb or a verb phrase saying what the method does: close ,
readPersons . The name should also suggest if the method is mutating the object or returning a new one.
For instance sort is sorting a collection in place, while sorted is returning a sorted copy of the collection.
The names should make it clear what the purpose of the entity is, so it's best to avoid using meaningless
words ( Manager , Wrapper etc.) in names.
When using an acronym as part of a declaration name, capitalize it if it consists of two letters ( IOStream );
capitalize only the rst letter if it is longer ( XmlFormatter , HttpInputStream ).
Formatting
Use four spaces for indentation. Do not use tabs.
For curly braces, put the opening brace in the end of the line where the construct begins, and the closing
brace on a separate line aligned horizontally with the opening construct.
if (elements != null) {
for (element in elements) {
// ...
}
}
In Kotlin, semicolons are optional, and therefore line breaks are signi cant. The language design
assumes Java-style braces, and you may encounter surprising behavior if you try to use a di erent
formatting style.
Horizontal whitespace
Put spaces around binary operators ( a + b ). Exception: don't put spaces around the "range to" operator
( 0..i ).
Put spaces between control ow keywords ( if , when , for and while ) and the corresponding opening
parenthesis.
Do not put a space before an opening parenthesis in a primary constructor declaration, method declaration
or method call.
class A(val x: Int)
fun bar() {
foo(1)
}
122
Put a space after // : // This is a comment
Do not put spaces around angle brackets used to specify type parameters: class Map<K, V> { ... }
As a general rule, avoid horizontal alignment of any kind. Renaming an identi er to a name with a di erent
length should not a ect the formatting of either the declaration or any of the usages.
Colon
Don't put a space before : when it separates a declaration and its type.
Classes with a few primary constructor parameters can be written in a single line:
class Person(id: Int, name: String)
Classes with longer headers should be formatted so that each primary constructor parameter is in a
separate line with indentation. Also, the closing parenthesis should be on a new line. If we use inheritance,
then the superclass constructor call or list of implemented interfaces should be located on the same line as
the parenthesis:
class Person(
id: Int,
name: String,
surname: String
) : Human(id, name) { /*...*/ }
For multiple interfaces, the superclass constructor call should be located rst and then each interface
should be located in a di erent line:
class Person(
id: Int,
name: String,
surname: String
) : Human(id, name),
KotlinMaker { /*...*/ }
123
For classes with a long supertype list, put a line break after the colon and align all supertype names
horizontally:
class MyFavouriteVeryLongClassHolder :
MyLongHolder<MyFavouriteVeryLongClass>(),
SomeOtherInterface,
AndAnotherOne {
To clearly separate the class header and body when the class header is long, either put a blank line
following the class header (as in the example above), or put the opening curly brace on a separate line:
class MyFavouriteVeryLongClassHolder :
MyLongHolder<MyFavouriteVeryLongClass>(),
SomeOtherInterface,
AndAnotherOne
{
fun foo() { /*...*/ }
}
Rationale: This ensures that properties declared in the primary constructor have the same indentation
as properties declared in the body of a class.
Modi ers
If a declaration has multiple modi ers, always put them in the following order:
public / protected / private / internal
expect / actual
final / open / abstract / sealed / const
external
override
lateinit
tailrec
vararg
suspend
inner
enum / annotation / fun // as a modifier in `fun interface`
companion
inline
infix
operator
data
Unless you're working on a library, omit redundant modi ers (e.g. public ).
Annotation formatting
Annotations are typically placed on separate lines, before the declaration to which they are attached, and
with the same indentation:
@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude
124
Annotations without arguments may be placed on the same line:
@JsonExclude @JvmField
var x: String
A single annotation without arguments may be placed on the same line as the corresponding declaration:
@Test fun foo() { /*...*/ }
File annotations
File annotations are placed after the le comment (if any), before the package statement, and are
separated from package with a blank line (to emphasize the fact that they target the le and not the
package).
/** License, copyright and whatever */
@file:JvmName("FooBar")
package foo.bar
Function formatting
If the function signature doesn't t on a single line, use the following syntax:
fun longMethodName(
argument: ArgumentType = defaultValue,
argument2: AnotherArgumentType,
): ReturnType {
// body
}
Prefer using an expression body for functions with the body consisting of a single expression.
fun foo(): Int { // bad
return 1
}
If the function has an expression body whose rst line doesn't t on the same line as the declaration, put
the = sign on the rst line, and indent the expression body by four spaces.
fun f(x: String, y: String, z: String) =
veryLongFunctionCallWithManyWords(andLongParametersToo(), x, y, z)
Property formatting
For more complex properties, always put get and set keywords on separate lines:
val foo: String
get() { /*...*/ }
125
For properties with an initializer, if the initializer is long, add a line break after the equals sign and indent
the initializer by four spaces:
private val defaultCharset: Charset? =
EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
If the condition of an if or when statement is multiline, always use curly braces around the body of the
statement. Indent each subsequent line of the condition by four spaces relative to statement begin. Put the
closing parentheses of the condition together with the opening curly brace on a separate line:
if (!component.isSyncing &&
!hasAnyKotlinRuntimeInScope(module)
) {
return createKotlinNotConfiguredPanel(module)
}
Rationale: Tidy alignment and clear separation of condition and statement body
Put the else , catch , finally keywords, as well as the while keyword of a do/while loop, on the same
line as the preceding curly brace:
if (condition) {
// body
} else {
// else part
}
try {
// body
} finally {
// cleanup
}
In a when statement, if a branch is more than a single line, consider separating it from adjacent case blocks
with a blank line:
private fun parsePropertyValue(propName: String, token: Token) {
when (token) {
is Token.ValueToken ->
callback.visitValue(propName, token.value)
Put short branches on the same line as the condition, without braces.
when (foo) {
true -> bar() // good
false -> { baz() } // bad
}
In long argument lists, put a line break after the opening parenthesis. Indent arguments by 4 spaces. Group
multiple closely related arguments on the same line.
126
drawSquare(
x = 10, y = 10,
width = 100, height = 100,
fill = true
)
Put spaces around the = sign separating the argument name and value.
When wrapping chained calls, put the . character or the ?. operator on the next line, with a single indent:
val anchor = owner
?.firstChild!!
.siblings(forward = true)
.dropWhile { it is PsiComment || it is PsiWhiteSpace }
The rst call in the chain usually should have a line break before it, but it's OK to omit it if the code makes
more sense that way.
Lambda formatting
In lambda expressions, spaces should be used around the curly braces, as well as around the arrow which
separates the parameters from the body. If a call takes a single lambda, it should be passed outside of
parentheses whenever possible.
list.filter { it > 10 }
If assigning a label for a lambda, do not put a space between the label and the opening curly brace:
fun foo() {
ints.forEach lit@{
// ...
}
}
When declaring parameter names in a multiline lambda, put the names on the rst line, followed by the
arrow and the newline:
appendCommaSeparated(properties) { prop ->
val propertyValue = prop.get(obj) // ...
}
If the parameter list is too long to t on a line, put the arrow on a separate line:
foo {
context: Context,
environment: Env
->
context.configureEnv(environment)
}
Trailing commas
A trailing comma is a comma symbol after the last item of a series of elements:
class Person(
val firstName: String,
val lastName: String,
val age: Int, // trailing comma
)
127
— It makes version-control di s cleaner – as all the focus is on the changed value.
— It makes it easy to add and reorder elements – there is no need to add or delete the comma if you
manipulate elements.
— It simpli es code generation, for example, for object initializers. The last element can also have a comma.
Trailing commas are entirely optional – your code will still work without them. The Kotlin style guide
encourages the use of trailing commas at the declaration site and leaves it at your discretion for the call site.
To enable trailing commas in the IntelliJ IDEA formatter, go to Settings | Editor | Code Style | Kotlin, open
the Other tab and select the Use trailing comma option.
— Enumerations
— Value arguments
— Class properties and parameters
— Function value parameters
— Parameters with optional type (including setters)
— Indexing su x
— Lambda parameters
— when entry
Enumerations
Value arguments
shift(
25,
20, // trailing comma
)
128
class Customer(
val name: String,
val lastName: String, // trailing comma
)
class Customer(
val name: String,
lastName: String, // trailing comma
)
fun powerOf(
number: Int,
exponent: Int, // trailing comma
) { /*...*/ }
constructor(
x: Comparable<Number>,
y: Iterable<Number>, // trailing comma
) {}
fun print(
vararg quantity: Int,
description: String, // trailing comma
) {}
Indexing su x
class Surface {
operator fun get(x: Int, y: Int) = 2 * x + 4 * y - 10
}
fun getZValue(mySurface: Surface, xValue: Int, yValue: Int) =
mySurface[
xValue,
yValue, // trailing comma
]
Lambda parameters
fun main() {
val x = {
x: Comparable<Number>,
y: Iterable<Number>, // trailing comma
->
println("1")
}
println(x)
}
when entry
129
fun isReferenceApplicable(myReference: KClass<*>) = when (myReference) {
Comparable::class,
Iterable::class,
String::class, // trailing comma
-> true
else -> false
}
@ApplicableFor([
"serializer",
"balancer",
"database",
"inMemoryCache", // trailing comma
])
fun run() {}
Type arguments
fun main() {
foo<
Comparable<Number>,
Iterable<Number>, // trailing comma
>()
}
Type parameters
class MyMap<
MyKey,
MyValue, // trailing comma
> {}
Destructuring declarations
data class Car(val manufacturer: String, val model: String, val year: Int)
val myCar = Car("Tesla", "Y", 2019)
val (
manufacturer,
model,
year, // trailing comma
) = myCar
Documentation comments
130
For longer documentation comments, place the opening /** on a separate line and begin each
subsequent line with an asterisk:
/**
* This is a documentation comment
* on multiple lines.
*/
Generally, avoid using @param and @return tags. Instead, incorporate the description of parameters and
return values directly into the documentation comment, and add links to parameters wherever they are
mentioned. Use @param and @return only when a lengthy description is required which doesn't t into
the ow of the main text.
// Avoid doing this:
/**
* Returns the absolute value of the given number.
* @param number The number to return the absolute value for.
* @return The absolute value.
*/
fun abs(number: Int) { /*...*/ }
// Do this instead:
/**
* Returns the absolute value of the given [number].
*/
fun abs(number: Int) { /*...*/ }
Unit
Semicolons
String templates
Don't use curly braces when inserting a simple variable into a string template. Use curly braces only for
longer expressions.
println("$name has ${children.size} children")
Immutability
131
Prefer using immutable data to mutable. Always declare local variables and properties as val rather than
var if they are not modi ed after initialization.
Always use immutable collection interfaces ( Collection , List , Set , Map ) to declare collections which
are not mutated. When using factory functions to create collection instances, always use functions that
return immutable collection types when possible:
// Bad: use of mutable collection type for value which will not be mutated
fun validateValue(actualValue: String, allowedValues: HashSet<String>) { ... }
Prefer declaring functions with default parameter values to declaring overloaded functions.
// Bad
fun foo() = foo("a")
fun foo(a: String) { /*...*/ }
// Good
fun foo(a: String = "a") { /*...*/ }
Type aliases
If you have a functional type or a type with type parameters which is used multiple times in a codebase,
prefer de ning a type alias for it:
typealias MouseClickHandler = (Any, MouseEvent) -> Unit
typealias PersonIndex = Map<String, Person>
If you use a private or internal type alias for avoiding name collision, prefer the import … as …
mentioned in Packages and Imports.
Lambda parameters
In lambdas which are short and not nested, it's recommended to use the it convention instead of
declaring the parameter explicitly. In nested lambdas with parameters, parameters should be always
declared explicitly.
Returns in a lambda
Avoid using multiple labeled returns in a lambda. Consider restructuring the lambda so that it will have a
single exit point. If that's not possible or not clear enough, consider converting the lambda into an
anonymous function.
Named arguments
132
Use the named argument syntax when a method takes multiple parameters of the same primitive type, or
for parameters of Boolean type, unless the meaning of all parameters is absolutely clear from context.
drawSquare(x = 10, y = 10, width = 100, height = 100, fill = true)
return when(x) {
0 -> "zero"
else -> "nonzero"
}
when(x) {
0 -> return "zero"
else -> return "nonzero"
}
if versus when
If you need to use a nullable Boolean in a conditional statement, use if (value == true) or if
(value == false) checks.
Using loops
Prefer using higher-order functions ( filter , map etc.) to loops. Exception: forEach (prefer using a
regular for loop instead, unless the receiver of forEach is nullable or forEach is used as part of a
longer call chain).
When making a choice between a complex expression using multiple higher-order functions and a loop,
understand the cost of the operations being performed in each case and keep performance considerations
in mind.
Loops on ranges
133
Use the until function to loop over an open range:
for (i in 0..n - 1) { /*...*/ } // bad
for (i in 0 until n) { /*...*/ } // good
Using strings
Prefer to use multiline strings instead of embedding \n escape sequences into regular string literals.
To maintain indentation in multiline strings, use trimIndent when the resulting string does not require
any internal indentation, or trimMargin when internal indentation is required:
assertEquals(
"""
Foo
Bar
""".trimIndent(),
value
)
Functions vs Properties
In some cases functions with no arguments might be interchangeable with read-only properties. Although
the semantics are similar, there are some stylistic conventions on when to prefer one to another.
Use extension functions liberally. Every time you have a function that works primarily on an object, consider
making it an extension function accepting that object as a receiver. To minimize API pollution, restrict the
visibility of extension functions as much as it makes sense. As necessary, use local extension functions,
member extension functions, or top-level extension functions with private visibility.
Using in x functions
Declare a function as in x only when it works on two objects which play a similar role. Good examples:
and , to , zip . Bad example: add .
Factory functions
134
If you declare a factory function for a class, avoid giving it the same name as the class itself. Prefer using a
distinct name making it clear why the behavior of the factory function is special. Only if there is really no
special semantics, you can use the same name as the class.
Example:
class Point(val x: Double, val y: Double) {
companion object {
fun fromPolar(angle: Double, radius: Double) = Point(...)
}
}
If you have an object with multiple overloaded constructors that don't call di erent superclass constructors
and can't be reduced to a single constructor with default argument values, prefer to replace the overloaded
constructors with factory functions.
Platform types
A public function/method returning an expression of a platform type must declare its Kotlin type explicitly:
fun apiCall(): String = MyJavaApi.getProperty("name")
Any property (package-level or class-level) initialised with an expression of a platform type must declare its
Kotlin type explicitly:
class Person {
val name: String = MyJavaApi.getProperty("name")
}
A local value initialized with an expression of a platform type may or may not have a type declaration:
fun main() {
val name = MyJavaApi.getProperty("name")
println(name)
}
Kotlin provides a variety of functions to execute a block of code in the context of a given object: let , run ,
with , apply , and also . For the guidance on choosing the right scope function for your case, refer to
Scope Functions.
— Always explicitly specify member visibility (to avoid accidentally exposing declarations as public API)
— Always explicitly specify function return types and property types (to avoid accidentally changing the
return type when the implementation changes)
— Provide KDoc comments for all public members, with the exception of overrides that do not require any
new documentation (to support generating documentation for the library)
135
Basics
Basic Types
In Kotlin, everything is an object in the sense that we can call member functions and properties on any
variable. Some of the types can have a special internal representation - for example, numbers, characters
and booleans can be represented as primitive values at runtime - but to the user they look like ordinary
classes. In this section we describe the basic types used in Kotlin: numbers, characters, booleans, arrays, and
strings.
Numbers
Kotlin provides a set of built-in types that represent numbers.
For integer numbers, there are four types with di erent sizes and, hence, value ranges.
All variables initialized with integer values not exceeding the maximum value of Int have the inferred type
Int . If the initial value exceeds this value, then the type is Long . To specify the Long value explicitly,
append the su x L to the value.
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1
For oating-point numbers, Kotlin provides types Float and Double . According to the IEEE 754 standard,
oating point types di er by their decimal place, that is, how many decimal digits they can store. Float
re ects the IEEE 754 single precision, while Double provides double precision.