Kotlin Reference
Kotlin Reference
Kotlin docs 47
Is anything missing? 52
Multiplatform programming 53
Use cases 58
What's next? 59
Sample projects 59
Supported platforms 60
Sample projects 60
Videos 60
Community 60
Security 61
Next steps 62
Kotlin/JS frameworks 65
2
Get started with Kotlin/JS 67
Kotlin Native 67
Why Kotlin/Native? 68
Target platforms 68
Interoperability 68
How to start 69
Sample projects 69
Interactive editors 70
Libraries 72
Learning Kotlin 77
Language features 78
Kotlin/JVM 83
Kotlin/Native 84
Kotlin Multiplatform 87
Kotlin/JS 89
Gradle 90
Standard library 93
Serialization 1.3.0-RC 97
3
What's new in Kotlin 1.5.20 97
Kotlin/JVM 98
Kotlin/Native 100
Kotlin/JS 101
Gradle 102
Kotlin/JVM 106
Kotlin/Native 109
Kotlin/JS 110
Kotlin/JVM 126
Kotlin/Native 127
Kotlin/JS 127
Kotlin/Native 131
Contracts 132
4
Capturing when subject in a variable 134
@JvmDefault 137
Tooling 140
Tools 149
JavaScript 150
5
Kotlin 1.5.20 for KMM 164
Functions 177
Variables 178
6
Creating classes and instances 178
Comments 179
Ranges 182
Collections 183
Idioms 186
7
Execute a statement if null 190
if expression 191
Formatting 197
Numbers 214
Booleans 221
8
Characters 222
Strings 222
Arrays 224
If expression 229
Exceptions 236
Imports 239
9
Classes 240
Constructors 240
Inheritance 244
Inheritance 245
Properties 249
Interfaces 253
10
Visibility modifiers 257
Packages 257
Modules 259
Extensions 259
Copying 266
Variance 269
11
Inner classes 276
Members 279
Inheritance 280
Representation 280
Delegation 287
Functions 297
12
Tail recursive functions 303
noinline 312
Equality 330
13
Structural equality 331
Threading 333
Callbacks 334
Coroutines 335
Coroutines 336
Annotations 337
Usage 338
Constructors 338
Lambdas 339
Reflection 345
14
JVM dependency 345
Tutorials 353
Enjoy the results – update the logic only once 403
15
Examples 408
Transactions 431
Concurrency 439
Atomics 446
16
Low-level capabilities 449
Coroutines 450
Samples 454
FAQ 458
Be supportive 465
17
Targets 469
Compilations 471
18
Publish a multiplatform library 496
Summary 507
Targets 518
Compilations 529
Dependencies 532
19
Language settings 534
Operators 558
20
Inheritance from Java classes 559
Properties 561
Visibility 568
KClass 568
Null-safety 570
Create a RESTful web service with a database using Spring Boot – tutorial 572
21
Execute HTTP requests 580
22
Create an application 598
Run the application 600
Dependencies 607
CSS 613
Yarn 614
Troubleshooting 615
23
Exclude declarations from DCE 632
Convert JS- and React-related classes and interfaces to external interfaces 637
24
JavaScript modules
650
Browser targets 651
Example 655
25
Concurrency in Kotlin/Native 667
Workers 667
Examples 675
26
Bindings 687
27
Read C Strings in Kotlin 714
Usage 721
Mappings 721
Subclassing 728
C features 728
Unsupported 728
28
Produce binaries with debug info with Kotlin/Native compiler 751
Breakpoints 751
Stepping 753
29
KMM plugin releases 769
Update to the new release 770
Copy 788
Iterators 790
Range 793
Progression 794
Sequences 795
Construct 796
30
Sequence processing example 797
Map 802
Zip 802
Associate 803
Flatten 805
Partition 808
Grouping 810
Slice 811
Chunked 812
Windowed 812
31
Check element existence 816
Ordering 817
Filter 833
Distinctions 838
Functions 842
32
takeIf and takeUnless 845
Timeout 862
33
Async-style functions 868
Buffering 895
34
Launching flow 910
Flow and Reactive Streams 913
Channels 914
Pipelines 916
Fan-out 918
Fan-in 920
CoroutineExceptionHandler 925
Supervision 929
Actors 938
35
Selecting from channels 940
Serialization 956
Libraries 957
Formats 957
Gradle 965
36
Configuring dependencies 968
OSGi 981
Maven 983
Dependencies 983
OSGi 989
Ant 990
37
Targeting JavaScript with single source folder and metaInfo option 992
References 992
REPL 996
Differences between "Kotlin coding conventions" and "IntelliJ IDEA default code style" 998
38
Gradle 1018
Maven 1019
Gradle 1022
Maven 1022
Gradle 1024
Maven 1024
39
Supported annotations 1032
Gradle 1032
Maven 1033
Overview 1035
Resources 1037
Limitations 1043
Types 1046
Misc 1047
40
Details 1048
AnnotationValue 1049
Element 1049
ExecutableElement 1049
Parameterizable 1050
QualifiedNameable 1050
TypeElement 1050
TypeParameterElement 1051
VariableElement 1051
ArrayType 1051
DeclaredType 1052
ExecutableType 1052
IntersectionType 1052
TypeMirror 1052
TypeVariable 1053
WildcardType 1053
Elements 1053
Types 1054
Example 1 1056
Example 2 1057
Advanced 1060
41
Frequently asked questions 1060
Maven 1068
Gradle 1068
FAQ 1068
FAQ 1074
42
Is Kotlin an object-oriented language or a functional one? 1075
What advantages does Kotlin give me over the Java programming language? 1075
Install the EAP Plugin for IntelliJ IDEA or Android Studio 1080
43
Configure your build for EAP 1082
Contribution 1085
Libraries 1090
Tools 1107
44
Compatibility guide for Kotlin 1.4 1108
Tools 1129
Scope 1144
Structure 1145
Scope 1147
Classification 1151
45
Usage guidelines 1155
How can I get more involved with the Kotlin community? 1156
Security 1157
46
Kotlin
docs
Get started with Kotlin What's new in Kotlin
You can get started with Kotlin using an online editor. If Discover what features are available in the newest Kotlin
you already have an IDE or you are ready to install one, release. You can try out upcoming features in the preview
here are also some ways to begin using Kotlin on your versions before they are released.
local machine.
What's new in Kotlin 1.5.30
Get started with Kotlin
What's new in Kotlin plugin 2021.2
Try Kotlin online
Kotlin public roadmap
Get started with KMM in Android Studio
KMM plugin releases for Android Studio
Acquaint yourself with some of the concepts and the Find a way to learn Kotlin that works for you. There are lots
basics of Kotlin. of learning materials available from our team and other
authors.
Basic syntax
Kotlin by example
Basic types
Kotlin Koans
Control flow
Kotlin Basics track on JetBrains Academy
Null safety
Hands-on tutorials
Coroutines
EduTools in IntelliJ IDEA
Coding conventions
Books
47
Kotlin YouTube Channel Stay in touch and contribute
Our YouTube channel is packed with resources for learning If you are interested in what's going with Kotlin, join us on
Kotlin! Subscribe to stay updated for shows, the latest social media, and get involved.
news, and live events!
Contribute to Kotlin
Kotlin in Spring Framework
Participate in Early Access Program
Webinars with Experts
Join Kotlin Slack
Kotlin Multiplatform Multiverse
Follow Kotlin on Twitter
Competitive Programming
Chat on Reddit
Kotlin Standard Library
Participate in Stack Overflow discussions
Talking Kotlin Podcast
Get
started
with
Kotlin
Kotlin is a modern but already mature programming language aimed to make developers happier. It’s concise, safe,
interoperable with Java and other languages, and provides many ways to reuse code between multiple platforms for
productive programming.
Learn
Kotlin
fundamentals
If you're already familiar with one or more programming languages and want to learn Kotlin, start with these Kotlin
learning materials.
If Kotlin is your first programming language, we recommend starting with the Atomic Kotlin book or signing up for the
free Kotlin Basics track on JetBrains Academy.
Create
your
powerful
application
with
Kotlin
Backend app
Here is how you can take the first steps in developing Kotlin server-side applications.
48
1. Install the latest version of IntelliJ IDEA.
To start from scratch, create a basic JVM application with the IntelliJ IDEA project wizard.
If you prefer more robust examples, choose one of the frameworks below and create a project:
Spring Ktor
A mature family of frameworks with an established ecosystem A lightweight framework for those who value freedom in making
that is used by millions of developers worldwide. architectural decisions.
Create a RESTful web service with Spring Boot. Create HTTP APIs with Ktor.
Build web applications with Spring Boot and Kotlin. Create a WebSocket chat with Ktor.
Use Spring Boot with Kotlin and RSocket. Create an interactive website with Ktor.
3. Use Kotlin and third-party libraries in your application. Learn more about adding library and tool dependencies to your project.
The Kotlin standard library offers a lot of useful things such as collections or coroutines.
Take a look at the following third-party frameworks, libs and tools for Kotlin.
Slack: get an invite and join the #getting-started, #server, #spring, or #ktor channels.
6. Follow Kotlin on Twitter, Reddit, and Youtube, and don't miss any important ecosystem updates.
Here you'll learn how to develop and improve your cross-platform mobile application usingKotlin Multiplatform Mobile (KMM).
To start from scratch, create a basic KMM application with the project wizard.
If you have an existing Android application and want to make it cross-platform, complete theMake your Android application work
on iOS tutorial.
If you prefer real-life examples, clone and play with an existing project, for example the networking and data storage project from
the hands-on tutorial or any KMM sample.
3. Use a wide set of multiplatform libraries to implement the required business logic only once in the shared module. Learn more about
adding dependencies.
49
Library Details
Ktor Docs.
DateTime Docs.
Learn how KMM is used at Netflix, VWWare, Yandex, and many other companies.
Slack: get an invite and join the #getting-started and #multiplatform channels.
6. Follow Kotlin on Twitter, Reddit, and Youtube, and don't miss any important ecosystem updates.
Kotlin provides an ability to transpile your Kotlin code, the Kotlin standard library, and any compatible dependencies to JavaScript.
Here you'll learn how to develop and improve your frontend web application usingKotlin/JS.
To start from scratch, create a basic browser application with the IntelliJ IDEA project wizard.
If you prefer more robust examples, complete the Building Web Applications with React and Kotlin/JS hands-on tutorial. It
includes a sample project that can serve as a good starting point for your own projects, and contains useful snippets and
templates.
50
Library Details
kotlinx.browser The Kotlin library for accessing browser-specific functionality, including typical top-level objects such as
document and window.
kotlinx.html The Kotlin library for generating DOM elements using statically-typed HTML builders.
fritz2 A third-party lightweight, high-performance, independent library for building reactive web apps in Kotlin
that are heavily dependent on coroutines and flows.
Doodle A third-party vector-based UI framework that uses browser's capabilities to draw user interfaces.
Compose for Web, a The JetBrains framework that brings Google's Jetpack Compose UI toolkit to the browser (currently with
part of Compose Alpha stability).
Multiplatform
kotlin-wrappers Provide convenient abstractions and deep integrations for one of the most popular JavaScript frameworks.
Kotlin wrappers also provide support for a number of adjacent technologies like react-redux, react-router,
or styled-components.
Slack: get an invite and join the #getting-started and #javascript channels.
6. Follow Kotlin on Twitter, Reddit, and Youtube, and don't miss any important ecosystem updates.
Android app
If you want to start using Kotlin for Android development, readGoogle’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 outthis Udacity course.
Follow Kotlin on Twitter, Reddit, and Youtube, and don't miss any important ecosystem updates.
51
Multiplatform library
Support for multiplatform programming is one of Kotlin’s key benefits. It reduces time spent writing and maintaining the same code
for different platforms while retaining the flexibility and benefits of native programming.
If you prefer more robust examples, complete the Create and publish a multiplatform library tutorial. It shows how to create a
multiplatform library for JVM, JS, and Native platforms, test it and publish to a local Maven repository.
3. Use libraries in your application. Learn more about adding dependencies on libraries.
Library Details
Coroutines Docs.
DateTime Docs.
Slack: get an invite and join the #getting-started and #multiplatform channels.
6. Follow Kotlin on Twitter, Reddit, and Youtube, and don't miss any important ecosystem updates.
Is
anything
missing?
52
If anything is missing or seems confusing on this page, please share your feedback.
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 benefits. It reduces time spent writing and maintaining
the same code for different platforms while retaining the flexibility and benefits of native programming.
53
Kotlin Multiplatform
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-specific 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-specific versions of Kotlin. Platform-specific versions of Kotlin (Kotlin/JVM,
Kotlin/JS, Kotlin/Native) include extensions to the Kotlin language, and platform-specific libraries and tools.
54
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 different 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.
55
Hierarchical structure
56
Code shared for iOS targets
If you need to access platform-specific APIs from the shared code, use the Kotlin mechanism of expected and actual
declarations.
With this mechanism, a common source set defines 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.
57
Expect and actual declarations
//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
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.
58
Client
—
Server
Another scenario when code sharing may bring benefits 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
Get started with Kotlin Multiplatform Mobile (KMM)
Tutorials
Create your first 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 first 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 efficient implementation provided by a specific 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 (KMM) samples
KotlinConf app
Kotlin
Multiplatform
Mobile
59
Multiplatform Mobile projects are in Alpha. Language features and tooling may change in future Kotlin versions.
Kotlin Multiplatform Mobile (KMM) is an SDK designed to simplify creating cross-platform mobile applications. With the
help of KMM, you can share common code between iOS and Android apps and write platform-specific code only where
it’s necessary. For example, to implement a native UI or when working with platform-specific APIs.
Get
started
with
Kotlin
Multiplatform
Mobile
Get started with KMM if you're already familiar with the Kotlin language and want to try out Kotlin Multiplatform Mobile
(KMM). If you're new to Kotlin, get started with these tutorials.
If you want to introduce KMM to your team, look through our recommendations.
Supported
platforms
KMM supports the following mobile targets:
Apple iOS on ARM64 (iPhone 5s and newer), ARM32 (earlier models) platforms, and desktop simulators on both Intel-
based and Apple Silicon platforms
Apple watchOS on ARM64 (Apple Watch Series 4 and newer), ARM32 (earlier models) platforms, and desktop
simulators on both Intel-based and Apple Silicon platforms
KMM is built on top of the Kotlin Multiplatform technology, which supports other platforms inlcuding JavaScript, Linux,
WebAssembly, and more.
Sample
projects
Check our list of KMM sample projects for inspiration.
Videos
Learn KMM with Kotlin Multiplatform Multiverse videos on YouTube.
Community
60
Kotlin Slack: Get an invite and join the #multiplatform channel.
Security
We do our best to make sure our software is free of security vulnerabilities. To reduce the risk of introducing a
vulnerability, you can follow Kotlin security recommendations.
We are very eager and grateful to hear about any security issues you find. To report vulnerabilities that you discover in any
part of KMM, please post a message directly to our issue tracker or send us an email.
For further information on how our responsible disclosure process works, please check the JetBrains Coordinated
Disclosure Policy.
Kotlin
for
server
side
Kotlin is a great fit 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 benefits 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 offers framework-specific 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 first steps. Kotlin Koans offer a guide through the key features of the
language with a series of interactive exercises.
Frameworks
for
server-side
development
with
Kotlin
Spring makes use of Kotlin's language features to offer more concise APIs, starting with version 5.0. The online project
generator allows you to quickly generate a new project in Kotlin.
61
Vert.x, a framework for building reactive Web applications on the JVM, offers 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 offering 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.
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.
Deploying
Kotlin
server-side
applications
Kotlin applications can be deployed into any host that supports Java Web applications, including Amazon Web Services,
Google Cloud Platform and more.
To deploy Kotlin applications on Heroku, you can follow the official Heroku tutorial.
AWS Labs provides a sample project showing the use of Kotlin for writing AWS Lambda functions.
Google Cloud Platform offers 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.
Users
of
Kotlin
on
the
server
side
Corda is an open-source distributed ledger platform, supported by major banks, and built entirely in Kotlin.
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
For a more in-depth introduction to the language, check out the Kotlin documentation on this site and Kotlin Koans.
62
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.
Want to migrate from Java to Kotlin? Learn how to perform typical tasks with strings in Java and Kotlin.
Kotlin
for
Android
Android mobile development has been Kotlin-first 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 benefits 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.
Kotlin
for
JavaScript
Kotlin/JS provides the ability to transpile your Kotlin code, the Kotlin standard library, and any compatible dependencies
63
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
cases
for
Kotlin/JS
There are numerous ways that Kotlin/JS can be used. To provide you some inspiration, here's a non-exhaustive list of
scenarios in which you can use Kotlin/JS.
Kotlin/JS allows you to leverage powerful browser and web APIs in a type-safe fashion. Create, modify and interact
with elements in the Document Object Model (DOM), use Kotlin code to control the rendering of canvas or WebGL
components, and enjoy access to many more of the features supported in modern browsers.
Write full, type-safe React applications with Kotlin/JS using the kotlin-wrappers provided by JetBrains, which
provide convenient abstractions and deep integrations for one of the most popular JavaScript frameworks. kotlin-
wrappers also provides support for a select number of adjacent technologies like react-redux, react-router, or
styled-components. Interoperability with the JavaScript ecosystem also means that you can also use third-party
React components and component libraries.
Use the Kotlin/JS frameworks that take full advantage of Kotlin concepts, its expressive power and conciseness.
The Node.js target provided by Kotlin/JS enables you to create applications that run on a server or get executed on
serverless infrastructure. You benefit from the same advantages as other applications executing in a JavaScript
runtime, such as faster startup speed and a reduced memory footprint. With kotlinx-nodejs, you have typesafe
access to the Node.js API directly from your Kotlin code.
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.
You don't have to write your whole application in Kotlin/JS, either – you can also generate libraries 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
64
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 find out what works best for your
project.
Regardless of your specific 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.
Kotlin/JS
frameworks
Modern web development benefits significantly from frameworks that simplify building web applications. Here are
examples of popular web frameworks for Kotlin/JS written by different authors:
KVision
KVision is an object-oriented web framework that makes it possible to write applications in Kotlin/JS with ready-to-use
components that can be used as building blocks for your application’s user interface. You can use both reactive and
imperative programming models to build your frontend, use connectors for Ktor, Spring Boot, and other frameworks to
integrate it with your server-side applications, and share code using Kotlin Multiplatform.
For updates and discussions about the framework, join #kvision and #javascript channels in the Kotlin Slack.
fritz2
fritz2 is a standalone framework for building reactive web user interfaces. It provides its own type-safe DSL for building
and rendering HTML elements, and it makes use of Kotlin’s coroutines and flows to express components and their
data bindings. It provides state management, validation, routing, and more out of the box, and integrates with Kotlin
Multiplatform projects.
For updates and discussions about the framework, join the #fritz2 and #javascript channels in the Kotlin Slack.
Doodle
Doodle is a vector-based UI framework for Kotlin/JS. Doodle applications use the browser’s graphics capabilities to
65
draw user interfaces instead of relying on DOM, CSS, or Javascript. By using this approach, Doodle gives you precise
control over the rendering of arbitrary UI elements, vector shapes, gradients, and custom visualizations.
For updates and discussions about the framework, join #doodle and #javascript channels in the Kotlin Slack.
Compose
for
Web
Compose for Web, a part of Compose Multiplatform brings Google's Jetpack Compose UI toolkit to your browser. It
allows you to build reactive web user interfaces using the concepts introduced by Jetpack Compose. It provides a DOM
API to describe your website, as well as an experimental set of multiplatform layout primitives. Compose for Web also
gives you the option to share parts of your UI code and logic across Android, desktop, and web.
Compose for Web is in Alpha, which means it hasn't reached the final shape yet, but you can already implement a proof-
of-concept for your production applications.
You can find more information about Compose Multiplatform on its landing page.
Join the #compose-web channel on Kotlin Slack to discuss Compose for Web, or #compose for general Compose
Multiplatform discussions.
Kotlin/JS,
Today
and
Tomorrow
Want to know more about Kotlin/JS?
In this video, Kotlin Developer Advocate Sebastian Aigner will explain the main Kotlin/JS benefits to you, share some tips
and use cases, and also tell you about the plans and upcoming features for Kotlin/JS.
Gif
66
Get
started
with
Kotlin/JS
If you're new to Kotlin, a good first step would be to familiarize yourself with the basic syntax of the language.
To start using Kotlin for JavaScript, please refer to the Set up a Kotlin/JS project, or pick a hands-on lab from the next
section to work through.
Hands-on
labs
for
Kotlin/JS
Hands-on labs are long-form tutorials that help you get to know a technology by guiding you through a self-contained
project related to a specific topic.
They include sample projects, which can serve as jumping-off 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.
New
Kotlin/JS
IR
compiler
The new Kotlin/JS IR compiler (currently with Beta stability) comes with a number of improvements over the current
default compiler. For example, it improves the size of generated executables via dead code elimination and makes it
smoother to interoperate with the JavaScript ecosystem and its tooling. By generating TypeScript declaration files (d.ts)
from Kotlin code, the new compiler makes it easier to create “hybrid†applications that mix TypeScript and Kotlin
code, and leverage code-sharing functionality using Kotlin Multiplatform.
To learn more about the available features in the new Kotlin/JS IR compiler and how to try it for your project, visit the
Kotlin/JS IR compiler documentation page and the migration guide.
Join
the
Kotlin/JS
community
You can also join #javascript channel in the official Kotlin Slack and chat with the community and the team.
Kotlin
Native
67
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:
macOS
Linux
Windows (MinGW)
Android NDK
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
68
package.
Sharing
code
between
platforms
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.
How
to
start
Tutorials
and
documentation
New to Kotlin? Take a look at Getting started with Kotlin.
Recommended documentation:
Multiplatform documentation
C interop
Swift/Objective-C interop
Recommended tutorials:
Sample
projects
Kotlin Multiplatform Mobile samples
KotlinConf app
69
KotlinConf Spinner app
Kotlin
for
data
science
From building data pipelines to productionizing machine learning models, Kotlin can be a great choice for working with
data:
Static typing and null safety help create reliable, maintainable code that is easy to troubleshoot.
Being a JVM language, Kotlin gives you great performance and an ability to leverage an entire ecosystem of tried and
true Java libraries.
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 findings with colleagues, or
build up your data science and machine learning skills.
Jupyter
Kotlin
kernel
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.
70
Kotlin in Jupyter notebook
Check out Kotlin kernel's GitHub repo for installation instructions, documentation, and examples.
Zeppelin
Kotlin
interpreter
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.
71
Kotlin in Zeppelin notebook
Libraries
The ecosystem of libraries for data-related tasks created by the Kotlin community is rapidly expanding. Here are some
libraries that you may find useful:
Kotlin
libraries
Multik: multidimensional arrays in Kotlin. The library provides Kotlin-idiomatic, type- and dimension-safe API for
mathematical operations over multidimensional arrays. Multik offers swappable JVM and native computational
engines, and a combination of the two for optimal performance.
KotlinDL is a high-level Deep Learning API written in Kotlin and inspired by Keras. It offers 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 classifier, clustering, linear regression,
and much more.
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 filtering, 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
72
JVM, but also with JS and Python.
kravis is another library for the visualization of tabular data inspired by R's ggplot.
londogard-nlp-toolkit is a library that provides utilities when working with natural language processing such as
word/subword/sentence embeddings, word-frequencies, stopwords, stemming, and much more.
Java
libraries
Since Kotlin provides first-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 find more options in the Kotlin Data Science Resources digest from
Thomas Nield.
Kotlin
for
competitive
programming
This tutorial is designed both for competitive programmers that did not use Kotlin before and for Kotlin developers that
did not participate in any competitive programming events before. It assumes the corresponding programming skills.
Competitive programming is a mind sport where contestants write programs to solve precisely specified algorithmic
problems within strict constraints. Problems can range from simple ones that can be solved by any software developer
and require little code to get a correct solution, to complex ones that require knowledge of special algorithms, data
73
structures, and a lot of practice. While not being specifically designed for competitive programming, Kotlin incidentally fits
well in this domain, reducing the typical amount of boilerplate that a programmer needs to write and read while working
with the code almost to the level offered by dynamically-typed scripting languages, while having tooling and performance
of a statically-typed language.
See Get started with Kotlin/JVM on how to set up development environment for Kotlin. In competitive programming, a
single project is usually created and each problem's solution is written in a single source file.
Simple
example:
Reachable
Numbers
problem
Let's take a look at a concrete example.
Codeforces Round 555 was held on April 26th for 3rd Division, which means it had problems fit for any developer to try.
You can use this link to read the problems. The simplest problem in the set is the Problem A: Reachable Numbers. It asks
to implement a straightforward algorithm described in the problem statement.
We'd start solving it by creating a Kotlin source file with an arbitrary name. A.kt will do well. First, we need to implement a
function specified in the problem statement as:
Let's denote a function f(x) in such a way: we add 1 to x, then, while there is at least one trailing zero in the resulting
number, we remove that zero.
Kotlin is a pragmatic and unopinionated language, supporting both imperative and function programming styles without
pushing the developer towards either one. We can implement the function f in functional style, using such Kotlin features
as tail recursion:
Alternatively, we can write an imperative implementation of the function f using the traditional while loop and mutable
variables that are denoted in Kotlin with var:
Types in Kotlin are optional in many places due to pervasive use of type-inference, but every declaration still has a well-
defined static type that is known at compilation.
Now, all is left is to write the main function that reads the input and implements the rest of the algorithm that the problem
statement asks for — to compute the number of different integers that are produced while repeatedly applying function f
to the initial number n that is given in the standard input.
By default, Kotlin runs on JVM and gives direct access to a rich and efficient collections library with general-purpose
74
collections and data-structures like dynamically-sized arrays (ArrayList), hash-based maps and sets (HashMap/ HashSet),
tree-based ordered maps and sets (TreeMap/ TreeSet), etc. Using a hash-set of integers to track values that were already
reached while applying function f, the straightforward imperative version of a solution to the problem can be written as
shown below:
fun main() {
var n = readLine()!!.toInt() // read integer from the input
val reached = HashSet<Int>() // a mutable hash set
while (reached.add(n)) n = f(n) // iterate function f
println(reached.size) // print answer to the output
}
Note the use of Kotlin's null-assertion operator !! after the readLine() function call. Kotlin's readLine() function is defined to
return a nullable type String? and returns null on the end of the input, which explicitly forces the developer to handle the
case of missing input.
There is no need to handle the case of misformatted input in competitive programming. In competitive programming, an
input format is always precisely specified and the actual input cannot deviate from the input specification in the problem
statement. That's what the null-assertion operator !! essentially does — it asserts that the input string is present and
throws an exception otherwise. Likewise, the String.toInt() function throws an exception if the input string is not an
integer.
All online competitive programming events allow the use of pre-written code, so you can define your own library of utility
functions that are geared towards competitive programming to make your actual solution code somewhat easier to read
and write. You would then use this code as a template for your solutions. For example, you can define the following helper
functions for reading inputs in competitive programming:
Note the use of private visibility modifier here. While the concept of visibility modifier is not relevant for competitive
programming at all, it allows you to place multiple solution files based on the same template without getting an error for
conflicting public declarations in the same package.
Functional
operators
example:
Long
Number
problem
For more complicated problems, Kotlin's extensive library of functional operations on collections comes in handy to
minimize the boilerplate and turn the code into a linear top-to-bottom and left-to-right fluent data transformation pipeline.
For example, the Problem B: Long Number problem takes a simple greedy algorithm to implement and it can be written
using this style without a single mutable variable:
fun main() {
// read input
val n = readLine()!!.toInt()
val s = readLine()!!
val fl = readLine()!!.split(" ").map { it.toInt() }
75
// define local function f
fun f(c: Char) = '0' + fl[c - '1']
// greedily find first and last indices
val i = s.indexOfFirst { c -> f(c) > c }
.takeIf { it >= 0 } ?: s.length
val j = s.withIndex().indexOfFirst { (j, c) -> j > i && f(c) < c }
.takeIf { it >= 0 } ?: s.length
// compose and write the answer
val ans =
s.substring(0, i) +
s.substring(i, j).map { c -> f(c) }.joinToString("") +
s.substring(j)
println(ans)
}
In this dense code, in addition to collection transformations, you can see such handy Kotlin features as local functions
and the elvis operator ?: that allow to express idioms like "take the value if it is positive or else use length" with a concise
and readable expressions like .takeIf { it >= 0 } ?: s.length, yet it is perfectly fine with Kotlin to create additional mutable
variables and express the same code in imperative style, too.
To make reading the input in competitive programming tasks like this more concise, you can have the following list of
helper input-reading functions:
With these helpers, the part of code for reading input becomes simpler, closely following the input specification in the
problem statement line by line:
// read input
val n = readInt()
val s = readLn()
val fl = readInts()
Note that in competitive programming it is customary to give variables shorter names than it is typical in industrial
programming practice, since the code is to be written just once and not supported thereafter. However, these names are
usually still mnemonic — a for arrays, i, j, etc for indices, r, and c for row and column numbers in tables, x and y for
coordinates, etc. It is easier to keep the same names for input data as it is given in the problem statement. However, more
complex problems require more code which leads to using longer self-explanatory variable and function names.
More
tips
and
tricks
Competitive programming problems often have input like this:
In Kotlin this line can be concisely parsed with the following statement using destructuring declaration from a list of
integers:
76
val (n, k) = readInts()
It might be temping to use JVM's java.util.Scanner class to parse less structured input formats. Kotlin is designed to
interoperate well with JVM libraries, so that their use feels quite natural in Kotlin. However, beware that java.util.Scanner is
extremely slow. So slow, in fact, that parsing 10 5 or more integers with it might not fit into a typical 2 second time-limit,
which a simple Kotlin's split(" ").map { it.toInt() } would handle.
Writing output in Kotlin is usually straightforward with println(...) calls and using Kotlin's string templates. However, care
must be taken when output contains on order of 10 5 lines or more. Issuing so many println calls is too slow, since the
output in Kotlin is automatically flushed after each line. A faster way to write many lines from an array or a list is using
joinToString() function with "\n" as the separator, like this:
Learning
Kotlin
Kotlin is easy to learn, especially for those who already know Java. A short introduction to the basic syntax of Kotlin for
software developers can be found directly in the reference section of the web site starting from basic syntax.
IDEA has built-in Java-to-Kotlin converter. It can be used by people familiar with Java to learn the corresponding Kotlin
syntactic constructions, but it is not perfect and it is still worth familiarizing yourself with Kotlin and learning the Kotlin
idioms.
A great resource to study Kotlin syntax and API of the Kotlin standard library are Kotlin Koans.
What's
new
in
Kotlin
1.5.30
Release date: 24 August 2021
Kotlin 1.5.30 offers language updates including previews of future changes, various improvements in platform support
and tooling, and new standard library functions.
Language features, including experimental sealed when statements, changes in using opt-in requirement, and others
You can also find a short overview of the changes in the release blog post and this video:
77
Gif
Language
features
Kotlin 1.5.30 is presenting previews of future language changes and bringing improvements to the opt-in requirement
mechanism and type inference:
Exhaustive
when
statements
for
sealed
and
Boolean
subjects
Support for sealed (exhaustive) when statements 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.
An exhaustive when statement contains branches for all possible types or values of its subject or for some types plus an
else branch. In other words, it covers all possible cases.
78
We’re planning to prohibit non-exhaustive when statements soon to make the behavior consistent with when
expressions. To ensure smooth migration, you can configure the compiler to report warnings about non-exhaustive when
statements with a sealed class or a Boolean. Such warnings will appear by default in Kotlin 1.6 and will become errors
later.
fun main() {
val x: Mode = Mode.ON
when (x) {
Mode.ON -> println("ON")
}
// WARNING: Non exhaustive 'when' statements on sealed classes/interfaces
// will be prohibited in 1.7, add an 'OFF' or 'else' branch instead
To enable this feature in Kotlin 1.5.30, use language version 1.6. You can also change the warnings to errors by enabling
progressive mode.
Kotlin
kotlin {
sourceSets.all {
languageSettings.apply {
languageVersion = "1.6"
//progressiveMode = true // false by default
}
}
}
Groovy
kotlin {
sourceSets.all {
languageSettings {
languageVersion = '1.6'
//progressiveMode = true // false by default
}
79
}
}
Suspending
functions
as
supertypes
Support for suspending functions as supertypes 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.
Kotlin 1.5.30 provides a preview of the ability to use a suspend functional type as a supertype with some limitations.
Kotlin
kotlin {
sourceSets.all {
languageSettings.apply {
languageVersion = "1.6"
}
}
}
Groovy
kotlin {
sourceSets.all {
languageSettings {
languageVersion = '1.6'
}
}
}
You can’t mix an ordinary functional type and a suspend functional type as supertype. This is because of the
implementation details of suspend functional types in the JVM backend. They are represented in it as ordinary
functional types with a marker interface. Because of the marker interface, there is no way to tell which of the
superinterfaces are suspended and which are ordinary.
You can't use multiple suspend functional supertypes. If there are type checks, you also can’t use multiple
80
ordinary functional supertypes.
Requiring
opt-in
on
implicit
usages
of
experimental
APIs
The opt-in requirement mechanism is Experimental. It may change at any time. See how to opt-in. Use it only
for evaluation purposes. We would appreciate your feedback on it in YouTrack.
The author of a library can mark an experimental API as requiring opt-in to inform users about its experimental state. The
compiler raises a warning or error when the API is used and requires explicit consent to suppress it.
In Kotlin 1.5.30, the compiler treats any declaration that has an experimental type in the signature as experimental.
Namely, it requires opt-in even for implicit usages of an experimental API. For example, if the function’s return type is
marked as an experimental API element, a usage of the function requires you to opt-in even if the declaration is not
marked as requiring an opt-in explicitly.
// Library code
@MyDateTime
class DateProvider // A class requiring opt-in
// Client code
Changes
to
using
opt-in
requirement
annotations
with
different
targets
The opt-in requirement mechanism is Experimental. It may change at any time. See how to opt-in. Use it only
for evaluation purposes. We would appreciate your feedback on it in YouTrack.
Kotlin 1.5.30 presents new rules for using and declaring opt-in requirement annotations on different targets. The compiler
now reports an error for use cases that are impractical to handle at compile time. In Kotlin 1.5.30:
81
Marking local variables and value parameters with opt-in requirement annotations is forbidden at the use site.
Marking backing fields and getters is forbidden. You can mark the basic property instead.
Setting TYPE and TYPE_PARAMETER annotation targets is forbidden at the opt-in requirement annotation declaration
site.
Improvements
to
type
inference
for
recursive
generic
types
In Kotlin and Java, you can define a recursive generic type, which references itself in its type parameters. In Kotlin 1.5.30,
the Kotlin compiler can infer a type argument based only on upper bounds of the corresponding type parameter if it is a
recursive generic. This makes it possible to create various patterns with recursive generic types that are often used in
Java to make builder APIs.
// Kotlin 1.5.20
val containerA = PostgreSQLContainer<Nothing>(DockerImageName.parse("postgres:13-alpine")).apply
{
withDatabaseName("db")
withUsername("user")
withPassword("password")
withInitScript("sql/schema.sql")
}
// Kotlin 1.5.30
val containerB = PostgreSQLContainer(DockerImageName.parse("postgres:13-alpine"))
.withDatabaseName("db")
.withUsername("user")
.withPassword("password")
.withInitScript("sql/schema.sql")
You can enable the improvements by passing the -Xself-upper-bound-inference or the -language-version 1.6 compiler
options. See other examples of newly supported use cases in this YouTrack ticket.
Eliminating
builder
inference
restrictions
Builder inference is a special kind of type inference that allows you to infer the type arguments of a call based on type
information from other calls inside its lambda argument. This can be useful when calling generic builder functions such as
buildList() or sequence(): buildList { add("string") }.
Inside such a lambda argument, there was previously a limitation on using the type information that the builder inference
tries to infer. This means you can only specify it and cannot get it. For example, you cannot call get() inside a lambda
argument of buildList() without explicitly specified type arguments.
Kotlin 1.5.30 removes these limitations with the -Xunrestricted-builder-inference compiler option. Add this option to
enable previously prohibited calls inside a lambda argument of generic builder functions:
82
@kotlin.ExperimentalStdlibApi
val list = buildList {
add("a")
add("b")
set(1, null)
val x = get(1)
if (x != null) {
removeAt(1)
}
}
@kotlin.ExperimentalStdlibApi
val map = buildMap {
put("a", 1)
put("b", 1.1)
put("c", 2f)
}
Also, you can enable this feature with the -language-version 1.6 compiler option.
Kotlin/JVM
With Kotlin 1.5.30, Kotlin/JVM receives the following features:
See the Gradle section for Kotlin Gradle plugin updates on the JVM platform.
Instantiation
of
annotation
classes
Instantiation of annotation classes 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.
With Kotlin 1.5.30 you can now call constructors of annotation classes in arbitrary code to obtain a resulting instance. This
feature covers the same use cases as the Java convention that allows the implementation of an annotation interface.
83
}
Use the -language-version 1.6 compiler option to enable this feature. Note that all current annotation class limitations,
such as restrictions to define non- val parameters or members different from secondary constructors, remain intact.
Improved
nullability
annotation
support
configuration
The Kotlin compiler can read various types of nullability annotations to get nullability information from Java. This
information allows it to report nullability mismatches in Kotlin when calling Java code.
In Kotlin 1.5.30, you can specify whether the compiler reports a nullability mismatch based on the information from
specific types of nullability annotations. Just use the compiler option -Xnullability-annotations=@<package-name>:
<report-level>. In the argument, specify the fully qualified nullability annotations package and one of these report levels:
See the full list of supported nullability annotations along with their fully qualified package names.
Here is an example showing how to enable error reporting for the newly supported RxJava 3 nullability annotations: -
[email protected]:strict. Note that all such nullability mismatches are warnings by
default.
Kotlin/Native
Kotlin/Native has received various changes and improvements:
Deprecation of linkage against DLLs without import libraries for MinGW targets
Apple
silicon
support
Kotlin 1.5.30 introduces native support for Apple silicon.
Previously, the Kotlin/Native compiler and tooling required the Rosetta translation environment for working on Apple
silicon hosts. In Kotlin 1.5.30, the translation environment is no longer needed – the compiler and tooling can run on
Apple silicon hardware without requiring any additional actions.
84
We’ve also introduced new targets that make Kotlin code run natively on Apple silicon:
macosArm64
iosSimulatorArm64
watchosSimulatorArm64
tvosSimulatorArm64
They are available on both Intel-based and Apple silicon hosts. All existing targets are available on Apple silicon hosts as
well.
Note that in 1.5.30 we provide only basic support for Apple silicon targets in the kotlin-multiplatform Gradle plugin.
Particularly, the new simulator targets aren’t included in the ios, tvos, and watchos target shortcuts. Learn how to use
Apple silicon targets with the target shortcuts. We will keep working to improve the user experience with the new targets.
Improved
Kotlin
DSL
for
the
CocoaPods
Gradle
plugin
To use the new DSL, update your project to Kotlin 1.5.30, and specify the parameters in the cocoapods section of your
build.gradle(.kts) file:
cocoapods {
frameworkName = "MyFramework" // This property is deprecated
// and will be removed in future versions
// New DSL for framework configuration:
framework {
// All Framework properties are supported
// Framework name configuration. Use this property instead of
// deprecated 'frameworkName'
baseName = "MyFramework"
// Dynamic framework support
isStatic = false
// Dependency export
export(project(":anotherKMMModule"))
transitiveExport = true
// Bitcode embedding
embedBitcode(BITCODE)
}
}
85
Support custom names for Xcode configuration
The Kotlin CocoaPods Gradle plugin supports custom names in the Xcode build configuration. It will also help you if
you’re using special names for the build configuration in Xcode, for example Staging.
To specify a custom name, use the xcodeConfigurationToNativeBuildType parameter in the cocoapods section of your
build.gradle(.kts) file:
cocoapods {
// Maps custom Xcode configuration to NativeBuildType
xcodeConfigurationToNativeBuildType["CUSTOM_DEBUG"] = NativeBuildType.DEBUG
xcodeConfigurationToNativeBuildType["CUSTOM_RELEASE"] = NativeBuildType.RELEASE
}
This parameter will not appear in the podspec file. When Xcode runs the Gradle build process, the Kotlin CocoaPods
Gradle plugin will select the necessary native build type.
There’s no need to declare the Debug and Release configurations because they are supported by default.
Experimental
interoperability
with
Swift
5.5
async/await
Concurrency interoperability with Swift async/await is Experimental. It may be dropped or changed at any time.
You should use it only for evaluation purposes. We would appreciate your feedback on it in YouTrack.
We added support for calling Kotlin’s suspending functions from Objective-C and Swift in 1.4.0, and now we’re
improving it to keep up with a new Swift 5.5 feature – concurrency with async and await modifiers.
The Kotlin/Native compiler now emits the _Nullable_result attribute in the generated Objective-C headers for suspending
functions with nullable return types. This makes it possible to call them from Swift as async functions with the proper
nullability.
Note that this feature is experimental and can be affected in the future by changes in both Kotlin and Swift. For now,
we’re offering a preview of this feature that has certain limitations, and we are eager to hear what you think. Learn
more about its current state and leave your feedback in this YouTrack issue.
Improved
Swift/Objective-C
mapping
for
objects
and
companion
objects
Getting objects and companion objects can now be done in a way that is more intuitive for native iOS developers. For
example, if you have the following objects in Kotlin:
object MyObject {
val x = "Some value"
}
class MyClass {
86
companion object {
val x = "Some value"
}
}
To access them in Swift, you can use the shared and companion properties:
MyObject.shared
MyObject.shared.x
MyClass.companion
MyClass.Companion.shared
Deprecation
of
linkage
against
DLLs
without
import
libraries
for
MinGW
targets
LLD is a linker from the LLVM project, which we plan to start using in Kotlin/Native for MinGW targets because of its
benefits over the default ld.bfd – primarily its better performance.
However, the latest stable version of LLD doesn’t support direct linkage against DLL for MinGW (Windows) targets.
Such linkage requires using import libraries. Although they aren’t needed with Kotlin/Native 1.5.30, we’re adding a
warning to inform you that such usage is incompatible with LLD that will become the default linker for MinGW in the
future.
Please share your thoughts and concerns about the transition to the LLD linker in this YouTrack issue.
Kotlin
Multiplatform
1.5.30 brings the following notable updates to Kotlin Multiplatform:
Ability
to
use
custom
cinterop
libraries
in
shared
native
code
Kotlin Multiplatform gives you an option to use platform-dependent interop libraries in shared source sets. Before 1.5.30,
this worked only with platform libraries shipped with Kotlin/Native distribution. Starting from 1.5.30, you can use it with
your custom cinterop libraries. To enable this feature, add the kotlin.mpp.enableCInteropCommonization=true property in
your gradle.properties:
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.enableDependencyPropagation=false
kotlin.mpp.enableCInteropCommonization=true
87
Support
for
XCFrameworks
All Kotlin Multiplatform projects can now have XCFrameworks as an output format. Apple introduced XCFrameworks as a
replacement for universal (fat) frameworks. With the help of XCFrameworks you:
Can gather logic for all the target platforms and architectures in a single bundle.
Don't need to remove all unnecessary architectures before publishing the application to the App Store.
XCFrameworks is useful if you want to use your KMM framework for devices and simulators on Apple M1.
Kotlin
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
plugins {
kotlin("multiplatform")
}
kotlin {
val xcf = XCFramework()
ios {
binaries.framework {
baseName = "shared"
xcf.add(this)
}
}
watchos {
binaries.framework {
baseName = "shared"
xcf.add(this)
}
}
tvos {
binaries.framework {
baseName = "shared"
xcf.add(this)
}
}
}
Groovy
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFrameworkConfig
plugins {
id 'org.jetbrains.kotlin.multiplatform'
}
kotlin {
def xcf = XCFrameworkConfig(project)
ios {
88
binaries.framework {
baseName = "shared"
xcf.add(it)
}
}
watchos {
binaries.framework {
baseName = "shared"
xcf.add(it)
}
}
tvos {
binaries.framework {
baseName = "shared"
xcf.add(it)
}
}
}
When you declare XCFrameworks, these new Gradle tasks will be registered:
assembleXCFramework
assembleReleaseXCFramework
New
default
publishing
setup
for
Android
artifacts
Using the maven-publish Gradle plugin, you can publish your multiplatform library for the Android target by specifying
Android variant names in the build script. The Kotlin Gradle plugin will generate publications automatically.
Before 1.5.30, the generated publication metadata included the build type attributes for every published Android variant,
making it compatible only with the same build type used by the library consumer. Kotlin 1.5.30 introduces a new default
publishing setup:
If all Android variants that the project publishes have the same build type attribute, then the published variants won't
have the build type attribute and will be compatible with any build type.
If the published variants have different build type attributes, then only those with the release value will be published
without the build type attribute. This makes the release variants compatible with any build type on the consumer side,
while non-release variants will only be compatible with the matching consumer build types.
To opt-out and keep the build type attributes for all variants, you can set this Gradle property:
kotlin.android.buildTypeAttribute.keep=true.
Kotlin/JS
Two major improvements are coming to Kotlin/JS with 1.5.30:
89
JS IR compiler backend reaches Beta
JS
IR
compiler
backend
reaches
Beta
The IR-based compiler backend for Kotlin/JS, which was introduced in 1.4.0 in Alpha, has reached Beta.
Previously, we published the migration guide for the JS IR backend to help you migrate your projects to the new backend.
Now we would like to present the Kotlin/JS Inspection Pack IDE plugin, which displays the required changes directly in
IntelliJ IDEA.
Better
debugging
experience
for
applications
with
the
Kotlin/JS
IR
backend
Kotlin 1.5.30 brings JavaScript source map generation for the Kotlin/JS IR backend. This will improve the Kotlin/JS
debugging experience when the IR backend is enabled, with full debugging support that includes breakpoints, stepping,
and readable stack traces with proper source references.
Learn more about how to debug Kotlin/JS in the browser or IntelliJ IDEA Ultimate.
Gradle
As a part of our mission to improve the Kotlin Gradle plugin user experience, we’ve implemented the following
features:
Support for Java toolchains, which includes an ability to specify a JDK home with the UsesKotlinJavaToolchain
interface for older Gradle versions
Support
for
Java
toolchains
Gradle 6.7 introduced the "Java toolchains support" feature. Using this feature, you can:
Run compilations, tests, and executables using JDKs and JREs that are different from the Gradle ones.
With toolchains support, Gradle can autodetect local JDKs and install missing JDKs that Gradle requires for the build.
Now Gradle itself can run on any JDK and still reuse the build cache feature.
The Kotlin Gradle plugin supports Java toolchains for Kotlin/JVM compilation tasks. A Java toolchain:
The ability to set the jdkHome option directly has been deprecated.
90
Sets the kotlinOptions.jvmTarget to the toolchain's JDK version if the user didn’t set the jvmTarget option
explicitly. If the toolchain is not configured, the jvmTarget field uses the default value. Learn more about JVM target
compatibility.
Use the following code to set a toolchain. Replace the placeholder <MAJOR_JDK_VERSION> with the JDK version you
would like to use:
Kotlin
kotlin {
jvmToolchain {
(this as
JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // “8â€
}
}
Groovy
kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // “8â€
}
}
Note that setting a toolchain via the kotlin extension will update the toolchain for Java compile tasks as well.
You can set a toolchain via the java extension, and Kotlin compilation tasks will use it:
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)) // “8â€
}
}
For information about setting any JDK version for KotlinCompile tasks, look through the docs about setting the JDK
version with the Task DSL.
For Gradle versions from 6.1 to 6.6, use the UsesKotlinJavaToolchain interface to set the JDK home.
Ability
to
specify
JDK
home
with
UsesKotlinJavaToolchain
interface
All Kotlin tasks that support setting the JDK via kotlinOptions now implement the UsesKotlinJavaToolchain interface. To
set the JDK home, put a path to your JDK and replace the <JDK_VERSION> placeholder:
Kotlin
91
project.tasks
.withType<UsesKotlinJavaToolchain>()
.configureEach {
it.kotlinJavaToolchain.jdk.use(
"/path/to/local/jdk",
JavaVersion.<LOCAL_JDK_VERSION>
)
}
Groovy
project.tasks
.withType(UsesKotlinJavaToolchain.class)
.configureEach {
it.kotlinJavaToolchain.jdk.use(
'/path/to/local/jdk',
JavaVersion.<LOCAL_JDK_VERSION>
)
}
Use the UsesKotlinJavaToolchain interface for Gradle versions from 6.1 to 6.6. Starting from Gradle 6.7, use the Java
toolchains instead.
When using this feature, note that kapt task workers will only use process isolation mode, and the kapt.workers.isolation
property will be ignored.
Easier
way
to
explicitly
specify
Kotlin
daemon
JVM
arguments
In Kotlin 1.5.30, there’s a new logic for the Kotlin daemon’s JVM arguments. Each of the options in the following
list overrides the ones that came before it:
If nothing is specified, the Kotlin daemon inherits arguments from the Gradle daemon (as before). For example, in the
gradle.properties file:
org.gradle.jvmargs=-Xmx1500m -Xms=500m
If the Gradle daemon’s JVM arguments have the kotlin.daemon.jvm.options system property, use it as before:
org.gradle.jvmargs=-Dkotlin.daemon.jvm.options=-Xmx1500m -Xms=500m
kotlin.daemon.jvmargs=-Xmx1500m -Xms=500m
92
Kotlin
kotlin {
kotlinDaemonJvmArgs = listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC")
}
Groovy
kotlin {
kotlinDaemonJvmArgs = ["-Xmx486m", "-Xms256m", "-XX:+UseParallelGC"]
}
Kotlin
tasks
.matching { it.name == "compileKotlin" && it is CompileUsingKotlinDaemon }
.configureEach {
(this as CompileUsingKotlinDaemon).kotlinDaemonJvmArguments.set(listOf("-Xmx486m", "-
Xms256m", "-XX:+UseParallelGC"))
}
Groovy
tasks
.matching {
it.name == "compileKotlin" && it instanceof CompileUsingKotlinDaemon
}
.configureEach {
kotlinDaemonJvmArguments.set(["-Xmx1g", "-Xms512m"])
}
In this case a new Kotlin daemon instance can start on task execution. Learn more about the Kotlin
daemon’s interactions with JVM arguments.
For more information about the Kotlin daemon, see the Kotlin daemon and using it with Gradle.
Standard
library
Kotlin 1.5.30 is bringing improvements to the standard library’s Duration and Regex APIs:
93
Parsing Duration from String
Changing
Duration.toString()
output
The Duration API is Experimental. It may be dropped or changed at any time. Use it only for evaluation
purposes. We would appreciate hearing your feedback on it in YouTrack.
Before Kotlin 1.5.30, the Duration.toString() function would return a string representation of its argument expressed in the
unit that yielded the most compact and readable number value. From now on, it will return a string value expressed as a
combination of numeric components, each in its own unit. Each component is a number followed by the unit’s
abbreviated name: d, h, m, s. For example:
The way negative durations are represented has also been changed. A negative duration is prefixed with a minus sign (-),
and if it consists of multiple components, it is surrounded with parentheses: -12m and -(1h 30m).
Note that small durations of less than one second are represented as a single number with one of the subsecond units.
For example, ms (milliseconds), us (microseconds), or ns (nanoseconds): 140.884ms, 500us, 24ns. Scientific notation is
no longer used to represent them.
If you want to express duration in a single unit, use the overloaded Duration.toString(unit, decimals) function.
94
We recommend using Duration.toIsoString() in certain cases, including serialization and interchange.
Duration.toIsoString() uses the stricter ISO-8601 format instead of Duration.toString().
Parsing
Duration
from
String
The Duration API is Experimental. It may be dropped or changed at any time. Use it only for evaluation
purposes. We would appreciate hearing your feedback on it in this issue.
toString(unit, decimals).
toIsoString().
parseOrNull() and parseIsoStringOrNull(), which behave like the functions above but return null instead of throwing
IllegalArgumentException on invalid duration formats.
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@ExperimentalTime
fun main() {
//sampleStart
val isoFormatString = "PT1H30M"
val defaultFormatString = "1h 30m"
val singleUnitFormatString = "1.5h"
val invalidFormatString = "1 hour 30 minutes"
println(Duration.parse(isoFormatString)) // "1h 30m"
println(Duration.parse(defaultFormatString)) // "1h 30m"
println(Duration.parse(singleUnitFormatString)) // "1h 30m"
//println(Duration.parse(invalidFormatString)) // throws exception
println(Duration.parseOrNull(invalidFormatString)) // "null"
//sampleEnd
}
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@ExperimentalTime
95
fun main() {
//sampleStart
val isoFormatString = "PT1H30M"
val defaultFormatString = "1h 30m"
println(Duration.parseIsoString(isoFormatString)) // "1h 30m"
//println(Duration.parseIsoString(defaultFormatString)) // throws exception
println(Duration.parseIsoStringOrNull(defaultFormatString)) // "null"
//sampleEnd
}
Matching
with
Regex
at
a
particular
position
Regex.matchAt() and Regex.matchesAt() functions are Experimental. They may be dropped or changed at any
time. Use them only for evaluation purposes. We would appreciate hearing your feedback on them in YouTrack.
The new Regex.matchAt() and Regex.matchesAt() functions provide a way to check whether a regex has an exact match
at a particular position in a String or CharSequence.
fun main(){
//sampleStart
val releaseText = "Kotlin 1.5.30 is released!"
// regular expression: one digit, dot, one digit, dot, one or more digits
val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()
println(versionRegex.matchesAt(releaseText, 0)) // "false"
println(versionRegex.matchesAt(releaseText, 7)) // "true"
//sampleEnd
}
fun main(){
//sampleStart
val releaseText = "Kotlin 1.5.30 is released!"
val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()
println(versionRegex.matchAt(releaseText, 0)) // "null"
println(versionRegex.matchAt(releaseText, 7)?.value) // "1.5.30"
//sampleEnd
}
Splitting
Regex
to
a
sequence
96
Regex.splitToSequence() and CharSequence.splitToSequence(Regex) functions are Experimental. They may be
dropped or changed at any time. Use them only for evaluation purposes. We would appreciate hearing your
feedback on them in YouTrack.
The new Regex.splitToSequence() function is a lazy counterpart of split(). It splits the string around matches of the given
regex, but it returns the result as a Sequence so that all operations on this result are executed lazily.
fun main(){
//sampleStart
val colorsText = "green, red , brown&blue, orange, pink&green"
val regex = "[,\\s]+".toRegex()
val mixedColor = regex.splitToSequence(colorsText)
.onEach { println(it) }
.firstOrNull { it.contains('&') }
println(mixedColor) // "brown&blue"
//sampleEnd
}
Serialization
1.3.0-RC
kotlinx.serialization 1.3.0-RC is here with new JSON serialization capabilities:
What's
new
in
Kotlin
1.5.20
Release date: 24 June 2021
Kotlin 1.5.20 has fixes for issues discovered in the new features of 1.5.0, and it also includes various tooling
improvements.
You can find an overview of the changes in the release blog post and this video:
97
Gif
Kotlin/JVM
Kotlin 1.5.20 is receiving the following updates on the JVM platform:
Support for calling Java’s Lombok-generated methods within modules that have Kotlin and Java code
String
concatenation
via
invokedynamic
Kotlin 1.5.20 compiles string concatenations into dynamic invocations (invokedynamic) on JVM 9+ targets, thereby
keeping up with modern Java versions. More precisely, it uses StringConcatFactory.makeConcatWithConstants() for
string concatenation.
To switch back to concatenation via StringBuilder.append() used in previous versions, add the compiler option -Xstring-
concat=inline.
Learn how to add compiler options in Gradle, Maven, and the command-line compiler.
Support
for
JSpecify
nullness
annotations
The Kotlin compiler can read various types of nullability annotations to pass nullability information from Java to Kotlin.
Version 1.5.20 introduces support for the JSpecify project, which includes the standard unified set of Java nullness
annotations.
With JSpecify, you can provide more detailed nullability information to help Kotlin keep null-safety interoperating with
98
Java. You can set default nullability for the declaration, package, or module scope, specify parametric nullability, and
more. You can find more details about this in the JSpecify user guide.
// JavaClass.java
import *;
@NullMarked
public class JavaClass {
public String notNullableString() { return ""; }
public @Nullable String nullableString() { return ""; }
}
// Test.kt
fun kotlinFun() = with(JavaClass()) {
notNullableString().length // OK
nullableString().length // Warning: receiver nullability mismatch
}
In 1.5.20, all nullability mismatches according to the JSpecify-provided nullability information are reported as warnings.
Use the -Xjspecify-annotations=strict and -Xtype-enhancement-improvements-strict-mode compiler options to enable
strict mode (with error reporting) when working with JSpecify. Please note that the JSpecify project is under active
development. Its API and implementation can change significantly at any time.
Support
for
calling
Java’s
Lombok-generated
methods
within
modules
that
have
Kotlin
and
Java
code
The Lombok compiler plugin 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.
Kotlin 1.5.20 introduces an experimental Lombok compiler plugin. This plugin makes it possible to generate and use
Java’s Lombok declarations within modules that have Kotlin and Java code. Lombok annotations work only in Java
sources and are ignored if you use them in Kotlin code.
@Getter, @Setter
@Data
@With
@Value
99
We're continuing to work on this plugin. To find out the detailed current state, visit the Lombok compiler plugin's
README.
Currently, we don't have plans to support the @Builder annotation. However, we can consider this if you vote for @Builder
in YouTrack.
Kotlin/Native
Kotlin/Native 1.5.20 offers a preview of the new feature and the tooling improvements:
Opt-in
export
of
KDoc
comments
to
generated
Objective-C
headers
The ability to export KDoc comments to generated Objective-C headers 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.
You can now set the Kotlin/Native compiler to export the documentation comments (KDoc) from Kotlin code to the
Objective-C frameworks generated from it, making them visible to the frameworks’ consumers.
/**
* Prints the sum of the arguments.
* Properly handles the case when the sum doesn't fit in 32-bit integer.
*/
fun printSum(a: Int, b: Int) = println(a.toLong() + b)
/**
* Prints the sum of the arguments.
* Properly handles the case when the sum doesn't fit in 32-bit integer.
*/
+ (void)printSumA:(int32_t)a b:(int32_t)b __attribute__((swift_name("printSum(a:b:)")));
To try out this ability to export KDoc comments to Objective-C headers, use the -Xexport-kdoc compiler option. Add the
100
following lines to build.gradle(.kts) of the Gradle projects you want to export comments from:
Kotlin
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
compilations.get("main").kotlinOptions.freeCompilerArgs += "-Xexport-kdoc"
}
}
Groovy
kotlin {
targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget) {
compilations.get("main").kotlinOptions.freeCompilerArgs += "-Xexport-kdoc"
}
}
We’d be very grateful if you would share your feedback with us using this YouTrack ticket.
Compiler
bug
fixes
The Kotlin/Native compiler has received multiple bug fixes in 1.5.20. You can find the complete list in the changelog.
There is an important bug fix that affects compatibility: in previous versions, string constants that contained incorrect UTF
surrogate pairs were losing their values during compilation. Now such values are preserved. Application developers can
safely update to 1.5.20 – nothing will break. However, libraries compiled with 1.5.20 are incompatible with earlier
compiler versions. See this YouTrack issue for details.
Improved
performance
of
Array.copyInto()
inside
one
array
We’ve improved the way Array.copyInto() works when its source and destination are the same array. Now such
operations finish up to 20 times faster (depending on the number of objects being copied) due to memory management
optimizations for this use case.
Kotlin/JS
With 1.5.20, we’re publishing a guide that will help you migrate your projects to the new IR-based backend for
Kotlin/JS.
Migration
guide
for
the
JS
IR
backend
The new migration guide for the JS IR backend identifies issues you may encounter during migration and provides
solutions for them. If you find any issues that aren’t covered in the guide, please report them to our issue tracker.
101
Gradle
Kotlin 1.5.20 introduces the following features that can improve the Gradle experience:
Caching
for
annotation
processors'
classloaders
in
kapt
Caching for annotation processors' classloaders in kapt 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.
There is now a new experimental feature that makes it possible to cache the classloaders of annotation processors in
kapt. This feature can increase the speed of kapt for consecutive Gradle runs.
To enable this feature, use the following properties in your gradle.properties file:
Deprecation
of
the
kotlin.parallel.tasks.in.project
build
property
With this release, Kotlin parallel compilation is controlled by the Gradle parallel execution flag --parallel. Using this flag,
Gradle executes tasks concurrently, increasing the speed of compiling tasks and utilizing the resources more efficiently.
You no longer need to use the kotlin.parallel.tasks.in.project property. This property has been deprecated and will be
removed in the next major release.
Standard
library
Kotlin 1.5.20 changes the platform-specific implementations of several functions for working with characters and as a
result brings unification across platforms:
Support for all Unicode digits in Char.digitToInt() for Kotlin/Native and Kotlin/JS.
102
Support
for
all
Unicode
digits
in
Char.digitToInt()
in
Kotlin/Native
and
Kotlin/JS
Char.digitToInt() returns the numeric value of the decimal digit that the character represents. Before 1.5.20, the function
supported all Unicode digit characters only for Kotlin/JVM: implementations on the Native and JS platforms supported
only ASCII digits.
From now, both with Kotlin/Native and Kotlin/JS, you can call Char.digitToInt() on any Unicode digit character and get its
numeric representation.
fun main() {
//sampleStart
val ten = '\u0661'.digitToInt() + '\u0039'.digitToInt() // ARABIC-INDIC DIGIT ONE + DIGIT
NINE
println(ten)
//sampleEnd
}
Unification
of
Char.isLowerCase()/isUpperCase()
implementations
across
platforms
The functions Char.isUpperCase() and Char.isLowerCase() return a boolean value depending on the case of the character.
For Kotlin/JVM, the implementation checks both the General_Category and the Other_Uppercase/ Other_Lowercase
Unicode properties.
Prior to 1.5.20, implementations for other platforms worked differently and considered only the general category. In
1.5.20, implementations are unified across platforms and use both properties to determine the character case:
fun main() {
//sampleStart
val latinCapitalA = 'A' // has "Lu" general category
val circledLatinCapitalA = 'â’¶' // has "Other_Uppercase" property
println(latinCapitalA.isUpperCase() && circledLatinCapitalA.isUpperCase())
//sampleEnd
}
What's
new
in
Kotlin
1.5.0
Release date: 5 May 2021
Kotlin 1.5.0 introduces new language features, stable IR-based JVM compiler backend, performance improvements, and
evolutionary changes such as stabilizing experimental features and deprecating outdated ones.
You can also find an overview of the changes in the release blog post.
Language
features
Kotlin 1.5.0 brings stable versions of the new language features presented for preview in 1.4.30:
103
JVM records support
Inline classes
Detailed descriptions of these features are available in this blog post and the corresponding pages of Kotlin
documentation.
JVM
records
support
Java is evolving fast, and to make sure Kotlin remains interoperable with it, we’ve introduced support for one of its
latest features – record classes.
In Kotlin code, you can use Java record classes like you would use typical classes with properties.
To use a Kotlin class as a record in Java code, make it a data class and mark it with the @JvmRecord annotation.
@JvmRecord
data class User(val name: String, val age: Int)
Gif
Sealed
interfaces
Kotlin interfaces can now have the sealed modifier, which works on interfaces in the same way it works on classes: all
implementations of a sealed interface are known at compile time.
104
sealed interface Polygon
You can rely on that fact, for example, to write exhaustive when expressions.
Additionally, sealed interfaces enable more flexible restricted class hierarchies because a class can directly inherit more
than one sealed interface.
Gif
Package-wide
sealed
class
hierarchies
Sealed classes can now have subclasses in all files of the same compilation unit and the same package. Previously, all
subclasses had to appear in the same file.
Direct subclasses may be top-level or nested inside any number of other named classes, named interfaces, or named
objects.
105
The subclasses of a sealed class must have a name that is properly qualified – they cannot be local or anonymous
objects.
Inline
classes
Inline classes are a subset of value-based classes that only hold values. You can use them as wrappers for a value of a
certain type without the additional overhead that comes from using memory allocations.
Inline classes can be declared with the value modifier before the name of the class:
@JvmInline
value class Password(val s: String)
Gif
Kotlin/JVM
Kotlin/JVM has received a number of improvements, both internal and user-facing. Here are the most notable among
106
them:
Stable
JVM
IR
backend
The IR-based backend for the Kotlin/JVM compiler is now Stable and enabled by default.
Starting from Kotlin 1.4.0, early versions of the IR-based backend were available for preview, and it has now become the
default for language version 1.5. The old backend is still used by default for earlier language versions.
You can find more details about the benefits of the IR backend and its future development in this blog post.
If you need to use the old backend in Kotlin 1.5.0, you can add the following lines to the project’s configuration file:
In Gradle:
Kotlin
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
kotlinOptions.useOldBackend = true
}
Groovy
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile) {
kotlinOptions.useOldBackend = true
}
In Maven:
<configuration>
<args>
<arg>-Xuse-old-backend</arg>
</args>
</configuration>
107
New
default
JVM
target:
1.8
The default target version for Kotlin/JVM compilations is now 1.8. The 1.6 target is deprecated.
If you need a build for JVM 1.6, you can still switch to this target. Learn how:
in Gradle
in Maven
SAM
adapters
via
invokedynamic
Kotlin 1.5.0 now uses dynamic invocations (invokedynamic) for compiling SAM (Single Abstract Method) conversions:
The new implementation uses LambdaMetafactory.metafactory() and auxiliary wrapper classes are no longer generated
during compilation. This decreases the size of the application’s JAR, which improves the JVM startup performance.
To roll back to the old implementation scheme based on anonymous class generation, add the compiler option -Xsam-
conversions=class.
Learn how to add compiler options in Gradle, Maven, and the command-line compiler.
Lambdas
via
invokedynamic
Compiling plain Kotlin lambdas into invokedynamic is Experimental. It may be dropped or changed at any time.
Opt-in is required (see details below), and you should use it only for evaluation purposes. We would appreciate
hearing your feedback on it in YouTrack.
Kotlin 1.5.0 is introducing experimental support for compiling plain Kotlin lambdas (which are not converted to an
instance of a functional interface) into dynamic invocations (invokedynamic). The implementation produces lighter
binaries by using LambdaMetafactory.metafactory(), which effectively generates the necessary classes at runtime.
Currently, it has three limitations compared to ordinary lambda compilation:
Experimental reflect API does not support lambdas created with LambdaMetafactory.
To try this feature, add the -Xlambdas=indy compiler option. We’d be grateful if you could share your feedback on it
using this YouTrack ticket.
Learn how to add compiler options in Gradle, Maven, and command-line compiler.
108
Deprecation
of
@JvmDefault
and
old
Xjvm-default
modes
Prior to Kotlin 1.4.0, there was the @JvmDefault annotation along with -Xjvm-default=enable and -Xjvm-
default=compatibility modes. They served to create the JVM default method for any particular non-abstract member in the
Kotlin interface.
In Kotlin 1.4.0, we introduced the new Xjvm-default modes, which switch on default method generation for the whole
project.
In Kotlin 1.5.0, we are deprecating @JvmDefault and the old Xjvm-default modes: -Xjvm-default=enable and -Xjvm-
default=compatibility.
Improvements
to
handling
nullability
annotations
Kotlin supports handling type nullability information from Java with nullability annotations. Kotlin 1.5.0 introduces a
number of improvements for the feature:
It reads nullability annotations on type arguments in compiled Java libraries that are used as dependencies.
Varargs
Fields
If a nullability annotation has multiple targets applicable to a type, and one of these targets is TYPE_USE, then
TYPE_USE is preferred. For example, the method signature @Nullable String[] f() becomes fun f(): Array<String?>! if
@Nullable supports both TYPE_USE and METHOD as targets.
For these newly supported cases, using the wrong type nullability when calling Java from Kotlin produces warnings. Use
the -Xtype-enhancement-improvements-strict-mode compiler option to enable strict mode for these cases (with error
reporting).
Kotlin/Native
Kotlin/Native is now more performant and stable. The notable changes are:
Performance improvements
109
Deactivation of the memory leak checker
Performance
improvements
In 1.5.0, Kotlin/Native is receiving a set of performance improvements that speed up both compilation and execution.
Compiler caches are now supported in debug mode for linuxX64 (only on Linux hosts) and iosArm64 targets. With
compiler caches enabled, most debug compilations complete much faster, except for the first one. Measurements
showed about a 200% speed increase on our test projects.
To use compiler caches for new targets, opt in by adding the following lines to the project’s gradle.properties:
If you encounter any issues after enabling the compiler caches, please report them to our issue tracker YouTrack.
Deactivation
of
the
memory
leak
checker
The built-in Kotlin/Native memory leak checker has been disabled by default.
It was initially designed for internal use, and it is able to find leaks only in a limited number of cases, not all of them.
Moreover, it later turned out to have issues that can cause application crashes. So we’ve decided to turn off the
memory leak checker.
The memory leak checker can still be useful for certain cases, for example, unit testing. For these cases, you can enable it
by adding the following line of code:
Platform.isMemoryLeakCheckerActive = true
Note that enabling the checker for the application runtime is not recommended.
Kotlin/JS
Kotlin/JS is receiving evolutionary changes in 1.5.0. We’re continuing our work on moving the JS IR compiler backend
towards stable and shipping other updates:
110
Upgrade
to
webpack
5
The Kotlin/JS Gradle plugin now uses webpack 5 for browser targets instead of webpack 4. This is a major webpack
upgrade that brings incompatible changes. If you’re using a custom webpack configuration, be sure to check the
webpack 5 release notes.
Frameworks
and
libraries
for
the
IR
compiler
The Kotlin/JS IR compiler is in Alpha. It may change incompatibly and require manual migration in the future.
We would appreciate your feedback on it in YouTrack.
Along with working on the IR-based backend for Kotlin/JS compiler, we encourage and help library authors to build their
projects in both mode. This means they are able to produce artifacts for both Kotlin/JS compilers, therefore growing the
ecosystem for the new compiler.
Many well-known frameworks and libraries are already available for the IR backend: KVision, fritz2, doodle, and others. If
you’re using them in your project, you can already build it with the IR backend and see the benefits it brings.
If you’re writing your own library, compile it in the 'both' mode so that your clients can also use it with the new
compiler.
Kotlin
Multiplatform
In Kotlin 1.5.0, choosing a testing dependency for each platform has been simplified and it is now done automatically by
the Gradle plugin.
A new API for getting a char category is now available in multiplatform projects.
Standard
library
The standard library has received a range of changes and improvements, from stabilizing experimental parts to adding
new features:
111
Duration API changes
New API for getting a char category now available in multiplatform code
You can learn more about the standard library changes in this blog post.
Gif
Stable
unsigned
integer
types
The UInt, ULong, UByte, UShort unsigned integer types are now Stable. The same goes for operations on these types,
ranges, and progressions of them. Unsigned arrays and operations on them remain in Beta.
Stable
locale-agnostic
API
for
upper/lowercasing
text
This release brings a new locale-agnostic API for uppercase/lowercase text conversion. It provides an alternative to the
toLowerCase(), toUpperCase(), capitalize(), and decapitalize() API functions, which are locale-sensitive. The new API helps
you avoid errors due to different locale settings.
Earlier
versions 1.5.0
alternative
112
Earlier
versions 1.5.0
alternative
String.toUpperCase() String.uppercase()
String.toLowerCase() String.lowercase()
Earlier
versions 1.5.0
alternative
Char.toUpperCase() Char.uppercaseChar():
Char
Char.uppercase(): String
For Kotlin/JVM, there are also overloaded uppercase(), lowercase(), and titlecase() functions with an explicit
Locale parameter.
The old API functions are marked as deprecated and will be removed in a future release.
See the full list of changes to the text processing functions in KEEP.
Stable
char-to-integer
conversion
API
Starting from Kotlin 1.5.0, new char-to-code and char-to-digit conversion functions are Stable. These functions replace
the current API functions, which were often confused with the similar string-to-Int conversion.
The new API removes this naming confusion, making the code behavior more transparent and unambiguous.
This release introduces Char conversions that are divided into the following sets of clearly named functions:
Functions to get the integer code of Char and to construct Char from the given code:
113
val Char.code: Int
An extension function for Int to convert the non-negative single digit it represents to the corresponding Char
representation:
The old conversion APIs, including Number.toChar() with its implementations (all except Int.toChar()) and Char extensions
for conversion to a numeric type, like Char.toInt(), are now deprecated.
Stable
Path
API
The experimental Path API with extensions for java.nio.file.Path is now Stable.
Floored
division
and
the
mod
operator
New operations for modular arithmetics have been added to the standard library:
floorDiv() returns the result of floored division. It is available for integer types.
mod() returns the remainder of floored division (modulus). It is available for all numeric types.
These operations look quite similar to the existing division of integers and rem() function (or the % operator), but they work
differently on negative numbers:
a.floorDiv(b) differs from a regular / in that floorDiv rounds the result down (towards the lesser integer), whereas /
truncates the result to the integer closer to 0.
a.mod(b) is the difference between a and a.floorDiv(b) * b. It’s either zero or has the same sign as b, while a % b
can have a different one.
fun main() {
114
//sampleStart
println("Floored division -5/3: ${(-5).floorDiv(3)}")
println( "Modulus: ${(-5).mod(3)}")
println("Truncated division -5/3: ${-5 / 3}")
println( "Remainder: ${-5 % 3}")
//sampleEnd
}
Duration
API
changes
The Duration API is Experimental. It may be dropped or changed at any time. Use it only for evaluation
purposes. We would appreciate hearing your feedback on it in YouTrack.
There is an experimental Duration class for representing duration amounts in different time units. In 1.5.0, the Duration
API has received the following changes:
Internal value representation now uses Long instead of Double to provide better precision.
There is a new API for conversion to a particular time unit in Long. It comes to replace the old API, which operates with
Double values and is now deprecated. For example, Duration.inWholeMinutes returns the value of the duration
expressed as Long and replaces Duration.inMinutes.
There are new companion functions for constructing a Duration from a number. For example, Duration.seconds(Int)
creates a Duration object representing an integer number of seconds. Old extension properties like Int.seconds are
now deprecated.
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@ExperimentalTime
fun main() {
//sampleStart
val duration = Duration.milliseconds(120000)
println("There are ${duration.inWholeSeconds} seconds in ${duration.inWholeMinutes} minutes")
//sampleEnd
}
New
API
for
getting
a
char
category
now
available
in
multiplatform
code
Kotlin 1.5.0 introduces the new API for getting a character’s category according to Unicode in multiplatform projects.
Several functions are now available in all the platforms and in the common code.
Char.isDigit()
Char.isLetter()
115
Char.isLetterOrDigit()
fun main() {
//sampleStart
val chars = listOf('a', '1', '+')
val (letterOrDigitList, notLetterOrDigitList) = chars.partition { it.isLetterOrDigit() }
println(letterOrDigitList) // [a, 1]
println(notLetterOrDigitList) // [+]
//sampleEnd
}
Char.isLowerCase()
Char.isUpperCase()
Char.isTitleCase()
fun main() {
//sampleStart
val chars = listOf('Dž', 'Lj', 'Nj', 'Dz', '1', 'A', 'a', '+')
val (titleCases, notTitleCases) = chars.partition { it.isTitleCase() }
println(titleCases) // [Dž, Lj, Nj, Dz]
println(notTitleCases) // [1, A, a, +]
//sampleEnd
}
Char.isDefined()
Char.isISOControl()
The property Char.category and its return type enum class CharCategory, which indicates a char's general category
according to Unicode, are now also available in multiplatform projects.
New
collections
function
firstNotNullOf()
The new firstNotNullOf() and firstNotNullOfOrNull() functions combine mapNotNull() with first() or firstOrNull(). They map
the original collection with the custom selector function and return the first non-null value. If there is no such value,
firstNotNullOf() throws an exception, and firstNotNullOfOrNull() returns null.
fun main() {
//sampleStart
val data = listOf("Kotlin", "1.5")
println(data.firstNotNullOf(String::toDoubleOrNull))
println(data.firstNotNullOfOrNull(String::toIntOrNull))
//sampleEnd
}
116
Strict
version
of
String?.toBoolean()
Two new functions introduce case-sensitive strict versions of the existing String?.toBoolean():
String.toBooleanStrict() throws an exception for all inputs except the literals true and false.
String.toBooleanStrictOrNull() returns null for all inputs except the literals true and false.
fun main() {
//sampleStart
println("true".toBooleanStrict())
println("1".toBooleanStrictOrNull())
// println("1".toBooleanStrict()) // Exception
//sampleEnd
}
kotlin-test
library
The kotlin-test library introduces some new features:
Simplified
test
dependencies
usage
in
multiplatform
projects
Now you can use the kotlin-test dependency to add dependencies for testing in the commonTest source set, and the
Gradle plugin will infer the corresponding platform dependencies for each test source set:
kotlin-test-junit for JVM source sets, see automatic choice of a testing framework for Kotlin/JVM source sets
Additionally, you can use the kotlin-test dependency in any shared or platform-specific source set.
An existing kotlin-test setup with explicit dependencies will continue to work both in Gradle and in Maven.
Automatic
selection
of
a
testing
framework
for
Kotlin/JVM
source
sets
The Gradle plugin now chooses and adds a dependency on a testing framework automatically. All you need to do is add
117
the dependency kotlin-test in the common source set.
Gradle uses JUnit 4 by default. Therefore, the kotlin("test") dependency resolves to the variant for JUnit 4, namely kotlin-
test-junit:
Kotlin
kotlin {
sourceSets {
val commonTest by getting {
dependencies {
implementation(kotlin("test")) // This brings the dependency
// on JUnit 4 transitively
}
}
}
}
Groovy
kotlin {
sourceSets {
commonTest {
dependencies {
implementation kotlin("test") // This brings the dependency
// on JUnit 4 transitively
}
}
}
}
You can choose JUnit 5 or TestNG by calling useJUnitPlatform() or useTestNG() in the test task:
tasks {
test {
// enable TestNG support
useTestNG()
// or
// enable JUnit Platform (a.k.a. JUnit 5) support
useJUnitPlatform()
}
}
You can disable automatic testing framework selection by adding the line kotlin.test.infer.jvm.variant=false to the
project’s gradle.properties.
Assertion
function
updates
This release brings new assertion functions and improves the existing ones.
118
The kotlin-test library now has the following features:
You can use the new assertIs<T> and assertIsNot<T> to check the type of a value:
@Test
fun testFunction() {
val s: Any = "test"
assertIs<String>(s) // throws AssertionError mentioning the actual type of s if the
assertion fails
// can now print s.length because of contract in assertIs
println("${s.length}")
}
Because of type erasure, this assert function only checks whether the value is of the List type in the following example
and doesn't check whether it's a list of the particular String element type: assertIs<List<String>>(value).
Comparing the container content for arrays, sequences, and arbitrary iterables
There is a new set of overloaded assertContentEquals() functions for comparing content for different collections that
don’t implement structural equality:
@Test
fun test() {
val expectedArray = arrayOf(1, 2, 3)
val actualArray = Array(3) { it + 1 }
assertContentEquals(expectedArray, actualArray)
}
New overloads to assertEquals() and assertNotEquals() for Double and Float numbers
There are new overloads for the assertEquals() function that make it possible to compare two Double or Float numbers
with absolute precision. The precision value is specified as the third parameter of the function:
@Test
fun test() {
val x = sin(PI)
// precision parameter
val tolerance = 0.000001
assertEquals(0.0, x, tolerance)
}
You can now check whether the collection or element contains something with the assertContains() function. You can
use it with Kotlin collections and elements that have the contains() operator, such as IntRange, String, and others:
@Test
119
fun test() {
val sampleList = listOf<String>("sample", "sample2")
val sampleString = "sample"
assertContains(sampleList, sampleString) // element in collection
assertContains(sampleString, "amp") // substring in string
}
From now on, you can use these as inline functions, so it's possible to call suspend functions inside a lambda
expression:
@Test
fun test() = runBlocking<Unit> {
val deferred = async { "Kotlin is nice" }
assertTrue("Kotlin substring should be present") {
deferred.await() .contains("Kotlin")
}
}
kotlinx
libraries
Along with Kotlin 1.5.0, we are releasing new versions of the kotlinx libraries:
kotlinx.coroutines 1.5.0-RC
kotlinx.serialization 1.2.1
kotlinx-datetime 0.2.0
Coroutines
1.5.0-RC
kotlinx.coroutines 1.5.0-RC is here with:
And more
Starting with Kotlin 1.5.0, experimental coroutines are disabled and the -Xcoroutines=experimental flag is no longer
supported.
Learn more in the changelog and the kotlinx.coroutines 1.5.0 release blog post.
120
Gif
Serialization
1.2.1
kotlinx.serialization 1.2.1 is here with:
And more
Learn more in the changelog and the kotlinx.serialization 1.2.1 release blog post.
121
Gif
dateTime
0.2.0
kotlinx-datetime 0.2.0 is here with:
And more
Learn more in the changelog and the kotlinx-datetime 0.2.0 release blog post.
Migrating
to
Kotlin
1.5.0
IntelliJ IDEA and Android Studio will suggest updating the Kotlin plugin to 1.5.0 once it is available.
To migrate existing projects to Kotlin 1.5.0, just change the Kotlin version to 1.5.0 and re-import your Gradle or Maven
project. Learn how to update to Kotlin 1.5.0.
To start a new project with Kotlin 1.5.0, update the Kotlin plugin and run the Project Wizard from File | New | Project.
The new command-line compiler is available for downloading on the GitHub release page.
Kotlin 1.5.0 is a feature release and therefore can bring incompatible changes to the language. Find the detailed list of
such changes in the Compatibility Guide for Kotlin 1.5.
122
What's
new
in
Kotlin
1.4.30
Release date: 3 February 2021
Kotlin 1.4.30 offers 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 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 specific compiler
options. See the sections below for details.
Learn more about the new features preview in this blog post.
JVM
records
support
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 benefits 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)
123
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 flexible 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.
Another use-case: with sealed interfaces, you can inherit a class from two or more sealed superclasses.
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 modifier on interfaces. We’d be very grateful if you would share your
feedback with us using this YouTrack ticket.
124
Learn more about sealed interfaces.
Package-wide
sealed
class
hierarchies
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 flexible hierarchies. They can have subclasses in all files of the same compilation unit
and the same package. Previously, all subclasses had to appear in the same file.
Direct subclasses may be top-level or nested inside any number of other named classes, named interfaces, or named
objects. The subclasses of a sealed class must have a name that is properly qualified – they cannot be local 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.
Improved
inline
classes
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:
Since inline classes are value-based, you can define them using the value modifier. The inline and value modifiers are
now equivalent to each other. In future Kotlin versions, we’re planning to deprecate the inline modifier.
From now on, Kotlin requires the @JvmInline annotation before a class declaration for the JVM backend:
Inline classes can have init blocks. You can add code to be executed right after the class is instantiated:
125
@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:
@JvmName("computeUInt")
fun compute(x: UInt) { }
In this release, we’ve changed the mangling scheme for functions to fix 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 flag 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
JVM
IR
compiler
backend
reaches
Beta
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 configuration file:
In Gradle:
126
Kotlin
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile::class) {
kotlinOptions.useIR = true
}
Groovy
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile) {
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).
Apple
watchOS
64-bit
simulator
target
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.
Support
for
Xcode
12.2
libraries
We have added support for the new libraries delivered with Xcode 12.2. You can now use them from Kotlin code.
Kotlin/JS
127
Lazy
initialization
of
top-level
properties
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 significantly 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 official 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.
Gradle
project
improvements
Support
the
Gradle
configuration
cache
Starting with 1.4.30, the Kotlin Gradle plugin supports the configuration cache feature. It speeds up the build process:
once you run the command, Gradle executes the configuration 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
Locale-agnostic
API
for
upper/lowercasing
text
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 different
platform locale settings can affect 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
128
// new API
println("Needs to be capitalized".uppercase()) // NEEDS TO BE CAPITALIZED
Earlier
versions 1.4.30
alternative
String.toUpperCase() String.uppercase()
String.toLowerCase() String.lowercase()
Earlier
versions 1.4.30
alternative
Char.toUpperCase() Char.uppercaseChar():
Char
Char.uppercase(): String
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.
Clear
Char-to-code
and
Char-to-digit
conversions
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.
129
The current Char to numbers conversion functions, which return UTF-16 codes expressed in different 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:
Serialization
updates
Along with Kotlin 1.4.30, we are releasing kotlinx.serialization 1.1.0-RC, which includes some new features:
Inline
classes
serialization
support
Starting with Kotlin 1.4.30, you can make inline classes serializable:
@Serializable
inline class Color(val rgb: Int)
130
The serialization framework does not box serializable inline classes when they are used in other serializable classes.
Unsigned
primitive
type
serialization
support
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")))
}
What's
new
in
Kotlin
1.3
Release date: 29 October 2018
Coroutines
release
After some long and extensive battle testing, coroutines are now released! It means that from Kotlin 1.3 the language
support and the API are fully stable. Check out the new coroutines overview page.
Kotlin 1.3 introduces callable references on suspend-functions and support of coroutines in the reflection API.
Kotlin/Native
Kotlin 1.3 continues to improve and polish the Native target. See the Kotlin/Native overview for details.
Multiplatform
projects
In 1.3, we've completely reworked the model of multiplatform projects in order to improve expressiveness and flexibility,
and to make sharing common code easier. Also, Kotlin/Native is now supported as one of the targets!
In the old model, common and platform-specific code needed to be placed in separate modules, linked by expectedBy
dependencies. Now, common and platform-specific code is placed in different source roots of the same module,
making projects easier to configure.
131
There is now a large number of preset platform configurations for different supported platforms.
The dependencies configuration has been changed; dependencies are now specified separately for each source root.
Source sets can now be shared between an arbitrary subset of platforms (for example, in a module that targets JS,
Android and iOS, you can have a source set that is shared only between Android and iOS).
Contracts
The Kotlin compiler does extensive static analysis to provide warnings and reduce boilerplate. One of the most notable
features is smartcasts — with the ability to perform a cast automatically based on the performed type checks:
However, as soon as these checks are extracted in a separate function, all the smartcasts immediately disappear:
To improve the behavior in such cases, Kotlin 1.3 introduces experimental mechanism called contracts.
Contracts allow a function to explicitly describe its behavior in a way which is understood by the compiler. Currently, two
wide classes of cases are supported:
Improving smartcasts analysis by declaring the relation between a function's call outcome and the passed arguments
values:
132
fun synchronize(lock: Any?, block: () -> Unit) {
// It tells the compiler:
// "This function will invoke 'block' here and now, and exactly one time"
contract { callsInPlace(block, EXACTLY_ONCE) }
}
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 benefit 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.
Custom contracts are introduced by a call to contract stdlib function, which provides DSL scope:
See the details on the syntax as well as the compatibility notice in the KEEP.
133
Capturing
when
subject
in
a
variable
In Kotlin 1.3, it is now possible to capture the when subject into a variable:
fun Request.getBody() =
when (val response = executeRequest()) {
is Success -> response.body
is HttpError -> throw HttpException(response.status)
}
While it was already possible to extract this variable just before when, val in when has its scope properly restricted to the
body of when, and so preventing namespace pollution. See the full documentation on when here.
@JvmStatic
and
@JvmField
in
companions
of
interfaces
With Kotlin 1.3, it is possible to mark members of a companion object of interfaces with annotations @JvmStatic and
@JvmField. In the classfile, such members will be lifted to the corresponding interface and marked as static.
interface Foo {
companion object {
@JvmField
val answer: Int = 42
@JvmStatic
fun sayHello() {
println("Hello, world!")
}
}
}
interface Foo {
public static int answer = 42;
public static void sayHello() {
// ...
}
}
Nested
declarations
in
annotation
classes
In Kotlin 1.3, it is possible for annotations to have nested classes, interfaces, objects, and companions:
134
annotation class Bar
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!")
}
Functions
with
big
arity
In Kotlin, functional types are represented as generic classes taking a different number of parameters: Function0<R>,
Function1<P0, R>, Function2<P0, P1, R>, ... This approach has a problem in that this list is finite, and it currently ends
with Function22.
Kotlin 1.3 relaxes this limitation and adds support for functions with bigger arity:
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 fine, not compile anymore) can be introduced only in
the major releases (1.2, 1.3, etc.).
We believe that a lot of users could use a much faster cycle where critical compiler bug fixes arrive immediately, making
the code more safe and correct. So, Kotlin 1.3 introduces the progressive compiler mode, which can be enabled by
passing the argument -progressive to the compiler.
In the progressive mode, some fixes in language semantics can arrive immediately. All these fixes have two important
properties:
135
They preserve backward compatibility of source code with older compilers, meaning that all the code which is
compilable by the progressive compiler will be compiled fine by non-progressive one.
They only make code safer in some sense — e.g., some unsound smartcast can be forbidden, behavior of the
generated code may be changed to be more predictable/stable, and so on.
Enabling the progressive mode can require you to rewrite some of your code, but it shouldn't be too much — all the fixes
enabled under progressive are carefully handpicked, reviewed, and provided with tooling migration assistance. We expect
that the progressive mode will be a nice choice for any actively maintained codebases which are updated to the latest
language versions quickly.
Inline
classes
Inline classes are in Alpha. They may change incompatibly and require manual migration in the future. We
appreciate your feedback on it in YouTrack. 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:
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:
Unsigned
integers
Unsigned integers are in Beta. Their implementation is almost stable, but migration steps may be required in the
future. We'll do our best to minimize any changes you will have to make.
136
Kotlin 1.3 introduces unsigned integer types:
Most of the functionality of signed types are supported for unsigned counterparts too:
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 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 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 {
137
// 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-specific solutions like java.util.Random on JVM. This release fixes this issue by introducing the class
kotlin.random.Random, which is available on all platforms:
import kotlin.random.Random
fun main() {
//sampleStart
val number = Random.nextInt(42) // number is in range [0, limit)
println(number)
//sampleEnd
}
isNullOrEmpty
and
orEmpty
extensions
isNullOrEmpty and orEmpty extensions for some types are already present in stdlib. The first one returns true if the
receiver is null or empty, and the second one falls back to an empty instance if the receiver is null. Kotlin 1.3 provides
similar extensions on collections, maps, and arrays of objects.
Copy
elements
between
two
existing
arrays
The array.copyInto(targetArray, targetOffset, startIndex, endIndex) functions for the existing array types, including the
unsigned arrays, make it easier to implement array-based containers in pure Kotlin.
fun main() {
//sampleStart
val sourceArr = arrayOf("k", "o", "t", "l", "i", "n")
val targetArr = sourceArr.copyInto(arrayOfNulls<String>(6), 3, startIndex = 3, endIndex = 6)
println(targetArr.contentToString())
sourceArr.copyInto(targetArr, startIndex = 0, endIndex = 3)
println(targetArr.contentToString())
//sampleEnd
}
138
associateWith
It is quite a common situation to have a list of keys and want to build a map by associating each of these keys with some
value. It was possible to do it before with the associate { it to getValue(it) } function, but now we’re introducing a more
efficient and easy to explore alternative: keys.associateWith { getValue(it) }.
fun main() {
//sampleStart
val keys = 'a'..'f'
val map = keys.associateWith { it.toString().repeat(5).capitalize() }
map.forEach { println(it) }
//sampleEnd
}
ifEmpty
and
ifBlank
functions
Collections, maps, object arrays, char sequences, and sequences now have an ifEmpty function, which allows specifying
a fallback value that will be used instead of the receiver if it is empty:
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
}
Sealed
classes
in
reflection
We’ve added a new API to kotlin-reflect that can be used to enumerate all the direct subtypes of a sealed class,
namely KClass.sealedSubclasses.
Smaller
changes
139
Boolean type now has companion.
Tooling
Code
style
support
in
IDE
Kotlin 1.3 introduces support for the recommended code style in IntelliJ IDEA. Check out this page for the migration
guidelines.
kotlinx.serialization
kotlinx.serialization is a library which provides multiplatform support for (de)serializing objects in Kotlin. Previously, it was
a separate project, but since Kotlin 1.3, it ships with the Kotlin compiler distribution on par with the other compiler
plugins. The main difference is that you don't need to manually watch out for the Serialization IDE Plugin being
compatible with the Kotlin IDE plugin version you're using: now the Kotlin IDE plugin already includes serialization!
Even though kotlinx.serialization now ships with the Kotlin Compiler distribution, it is still considered to be an
experimental feature in Kotlin 1.3.
Scripting
update
Scripting 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.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 files. Scratch file is a kotlin script file with the .kts extension that
you can run and get evaluation results directly in the editor.
140
Consult the general Scratches documentation for details.
What's
new
in
Kotlin
1.2
Release date: 28 November 2017
Table
of
contents
Multiplatform projects
Standard library
JVM backend
JavaScript backend
Multiplatform
projects
(experimental)
Multiplatform projects are a new experimental feature in Kotlin 1.2, allowing you to reuse code between target platforms
supported by Kotlin – JVM, JavaScript, and (in the future) Native. In a multiplatform project, you have three kinds of
modules:
A common module contains code that is not specific 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 specific
platform, as well as other platform-dependent code.
A regular module targets a specific platform and can either be a dependency of platform modules or depend on
platform modules.
When you compile a multiplatform project for a specific platform, the code for both the common and platform-specific
parts is generated.
A key feature of the multiplatform project support is the possibility to express dependencies of common code on
platform-specific parts through expected and actual declarations. An expected declaration specifies 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:
141
fun greet() {
// usage of the expected API:
val greeting = hello("multiplatform world")
println(greeting)
}
See the multiplatform programming documentation for details and steps to build a multiplatform project.
Other
language
features
Array
literals
in
annotations
Starting with Kotlin 1.2, array arguments for annotations can be passed with the new array literal syntax instead of the
arrayOf function:
Lateinit
top-level
properties
and
local
variables
The lateinit modifier 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 defined later:
142
third = Node(3, next = { first })
Check
whether
a
lateinit
var
is
initialized
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
}
}
Inline
functions
with
default
functional
parameters
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) }
Information
from
explicit
casts
is
used
for
type
inference
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 specific 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:
143
val button = findViewById(R.id.button) as Button
Smart
cast
improvements
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:
Also, smart casts in a lambda are now allowed for local variables that are only modified before the lambda:
run {
if (x != null) {
println(x.length) // x is smart cast to String
}
}
//sampleEnd
}
Support
for
::foo
as
a
shorthand
for
this::foo
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.
144
Breaking
change:
sound
smart
casts
after
try
blocks
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 fixes 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 flag -Xlegacy-smart-cast-after-try as the compiler argument.
It will become deprecated in Kotlin 1.3.
Deprecation:
data
classes
overriding
copy
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 conflict has become deprecated with a warning in Kotlin 1.2 and will be an error in Kotlin
1.3.
Deprecation:
nested
types
in
enum
entries
Inside enum entries, defining 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.
Deprecation:
single
named
argument
for
vararg
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.
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.
Deprecation:
mutating
backing
field
of
a
read-only
property
Mutating the backing field 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
145
Kotlin
standard
library
artifacts
and
split
packages
The Kotlin standard library is now fully compatible with the Java 9 module system, which forbids split packages (multiple
jar files 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
different 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.
windowed,
chunked,
zipWithNext
New extensions for Iterable<T>, Sequence<T>, and CharSequence cover such use cases as buffering or batch
processing (chunked), sliding window and computing sliding average (windowed) , and processing pairs of subsequent
items (zipWithNext):
println("items: $items\n")
fill,
replaceAll,
shuffle/shuffled
A set of extension functions was added for manipulating lists: fill, replaceAll and shuffle for MutableList, and shuffled for
read-only List:
146
println("Items doubled: $items")
items.fill(5)
println("Items filled with 5: $items")
//sampleEnd
}
Math
operations
in
kotlin-stdlib
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.
Operators
and
conversions
for
BigInteger
and
BigDecimal
Kotlin 1.2 introduces a set of functions for operating with BigInteger and BigDecimal and creating them from other
numeric types. These are:
147
toBigDecimal for Int, Long, Float, Double, and BigInteger
Floating
point
to
bits
conversions
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 floating point numbers from the bit representation
Regex
is
now
serializable
The kotlin.text.Regex class has become Serializable and can now be used in serializable hierarchies.
Closeable.use
calls
Throwable.addSuppressed
if
available
The Closeable.use function calls Throwable.addSuppressed when an exception is thrown during closing the resource
after some other exception.
JVM
backend
Constructor
calls
normalization
Ever since version 1.0, Kotlin supported expressions with complex control flow, such as try-catch expressions and inline
function calls. Such code is valid according to the Java Virtual Machine specification. 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 compiler
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 affect overall performance of your application; use it only if you have some complex state
shared between multiple classes and updated on class initialization.
148
The “manual†workaround is to store the values of sub-expressions with control flow in variables, instead of
evaluating them directly inside the call arguments. It’s similar to -Xnormalize-constructor-calls=enable.
Java-default
method
calls
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.
Breaking
change:
consistent
behavior
of
x.equals(null)
for
platform
types
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).
To return to the pre-1.2 behavior, pass the flag -Xno-exception-on-explicit-equals-for-boxed-null to the compiler.
Breaking
change:
fix
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 flag -Xno-receiver-assertions to the compiler.
JavaScript
backend
TypedArrays
support
enabled
by
default
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
}
149
What's
new
in
Kotlin
1.1
Release date: 15 February 2016
Table
of
contents
Coroutines
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 specific programming paradigm or concurrency library.
A coroutine is effectively 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).
150
// and then show it in UI
showImage(image)
}
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 different 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:
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 coroutines 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 final 1.1 release.
Other
language
features
Type
aliases
A type alias allows you to define 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"
151
//sampleEnd
See the type aliases documentation and KEEP for more details.
Bound
callable
references
You can now use the :: operator to get a member reference pointing to a method or property of a specific 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
Sealed
and
data
classes
Kotlin 1.1 removes some of the restrictions on sealed and data classes that were present in Kotlin 1.0. Now you can
define subclasses of a top-level sealed class on the top level in the same file, and not just as nested classes of the sealed
class. Data classes can now extend other classes. This can be used to define a hierarchy of expression classes nicely and
cleanly:
//sampleStart
sealed class Expr
152
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
val e = eval(Sum(Const(1.0), Const(2.0)))
//sampleEnd
Read the sealed classes documentation or KEEPs for sealed class and data class 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:
Read the destructuring declarations documentation and KEEP for more details.
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:
//sampleStart
map.forEach { _, value -> println("$value!") }
//sampleEnd
}
153
val (_, status) = getResult()
//sampleEnd
println("status is '$status'")
}
Underscores
in
numeric
literals
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
Shorter
syntax
for
properties
For properties with the getter defined 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}")
}
Inline
property
accessors
You can now mark property accessors with the inline modifier if the properties don't have a backing field. 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
154
// the getter will be inlined
println("Last index of $list is ${list.lastIndex}")
}
You can also mark the entire property as inline- then the modifier is applied to both accessors.
Read the inline functions documentation and KEEP for more details.
Local
delegated
properties
You can now use the delegated property syntax with local variables. One possible use is defining a lazily evaluated local
variable:
import java.util.Random
Interception
of
delegated
property
binding
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:
class MyUI {
val image by bindResource(ResourceID.image_id)
val text by bindResource(ResourceID.text_id)
}
155
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.
Generic
enum
value
access
It is now possible to enumerate the values of an enum class in a generic way.
//sampleStart
enum class RGB { RED, GREEN, BLUE }
Scope
control
for
implicit
receivers
in
DSLs
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 defined on the implicit receiver of td will be available inside the
lambda passed to td. You do that by defining your annotation marked with the @DslMarker meta-annotation and applying
it to the base class of the tag classes.
Read the type safe builders documentation and KEEP for more details.
rem
operator
The mod operator is now deprecated, and rem is used instead. See this issue for motivation.
156
Standard
library
String
to
number
conversions
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.
Also integer conversion functions, like Int.toString(), String.toInt(), String.toIntOrNull(), each got an overload with radix
parameter, which allows to specify the base of conversion (2 to 36).
onEach()
onEach is a small, but useful extension function for collections and sequences, which allows to perform some action,
possibly with side-effects, 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(),
takeIf(),
and
takeUnless()
These are three general-purpose extension functions applicable to any receiver.
also is like apply: it takes the receiver, does some action on it, and returns that receiver. The difference 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
157
val copy = block.copy()
println("Testing the content was copied:")
println(block.content == copy.content)
}
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 writing constructs like:
//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
println("'$keyword' was found in '$input'")
println(input)
println(" ".repeat(index) + "^")
}
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:
It is also convenient to use when you have a callable reference instead of the lambda:
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:
158
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}.")
}
Map.toMap()
and
Map.toMutableMap()
These functions can be used for easy copying of maps:
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 fills 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.
minOf()
and
maxOf()
These functions can be used to find 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.
159
val longestList = maxOf(list1, list2, compareBy { it.size })
//sampleEnd
println("minSize = $minSize")
println("longestList = $longestList")
}
Array-like
List
instantiation
functions
Similar to the Array constructor, there are now functions that create List and MutableList instances and initialize each
element by calling a lambda:
println("squares: $squares")
println("mutable: $mutable")
}
Map.getValue()
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.
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.
160
Array
manipulation
functions
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).
JVM
Backend
Java
8
bytecode
support
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.
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.
Parameter
names
in
the
bytecode
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.
Mutable
closure
variables
The box classes used for capturing mutable closure variables in lambdas no longer have volatile fields. This change
improves performance, but can lead to new race conditions in some rare usage scenarios. If you're affected by this, you
need to provide your own synchronization for accessing the variables.
161
javax.script
support
Kotlin now integrates with the javax.script API (JSR-223). The API allows to evaluate snippets of code at runtime:
kotlin.reflect.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 reflection interfaces (such as KClass) are part of the Kotlin standard library, not kotlin-reflect, and
are not affected by the move.
JavaScript
backend
Unified
standard
library
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 defined 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.
Better
code
generation
JavaScript backend now generates more statically checkable code, which is friendlier to JS code processing tools, like
minifiers, optimisers, linters, etc.
The
external
modifier
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 modifier. (In Kotlin 1.0, the @native annotation was used instead.) Unlike the JVM target, the JS one
permits to use external modifier with classes and properties. For example, here's how you can declare the DOM Node
class:
// etc
}
162
Improved
import
handling
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:
@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 configured to use.
What's
new
in
Kotlin
for
KMM
KMM is part of the larger Kotlin ecosystem and leverages Kotlin features and improvements for a better mobile developer
experience. Every Kotlin release brings features and improvements that are helpful for mobile developers like you.
Android Studio will recommend an automatic update to a new Kotlin release. You can also update manually.
Here you can find a short summary of the features Kotlin provides for developing multiplatform mobile applications.
Kotlin
1.5.30
for
KMM
Kotlin 1.5.30 introduces a number of improvements and features that are helpful for KMM:
Apple silicon support. Kotlin 1.5.30 introduces native support for Apple silicon. Now the Kotlin/Native compiler and
163
tooling can run on Apple silicon hardware without Rosetta translation environment
Improved Kotlin DSL for CocoaPods Gradle plugin. Kotlin 1.5.30 introduces the improved CocoaPods Gradle plugin
DSL. In addition to the name of the framework, you can now specify other parameters in the pod configuration:
Bitcode embedding
Experimental interoperability with Swift 5.5 async/await. The Kotlin/Native compiler now emits the _Nullable_result
attribute in the generated Objective-C headers for suspending functions with nullable return types. This makes it
possible to call them from Swift as async functions with the proper nullability.
Improved Swift/Objective-C mapping for objects and companion objects. Getting objects and companion objects can
now be done in a way that is more intuitive for native iOS developers.
Sharing custom cinterop libraries between platforms. Starting from Kotlin 1.5.30, you can use custom cinterop libraries
in shared native code.
Support for XCFrameworks. Now all Kotlin Multiplatform projects can use XCFrameworks.
New default publishing setup for Android artifacts. Kotlin 1.5.30 brings new default publishing setup for Android
artefacts. You can publish your multiplatform library for the Android target by specifying Android variant names in the
build script. The Kotlin Gradle plugin will generate publications automatically.
Learn more about new default publishing setup for Android artifacts.
Kotlin
1.5.20
for
KMM
Kotlin 1.5.20 introduces a number of improvements and features that are helpful for KMM:
Export of KDoc comments to generated Objective-C headers. You can now set the Kotlin/Native compiler to export
the documentation comments (KDoc) from Kotlin code to the Objective-C frameworks generated from it, making them
visible to the frameworks’ consumers.
164
This feature is experimental. We would appreciate your feedback on it in YouTrack.
Learn more about exporting KDoc comments to generated Objective-C headers and how to opt in to this feature.
New framework-packing task for Kotlin/Native. The Kotlin Multiplatform Gradle plugin now includes the
embedAndSignAppleFrameworkForXcode task, which can be used from Xcode to connect KMM modules to the iOS
part of your project.
Check out this blog post to learn about the new framework-packing task and how to remove from the packForXcode
task from your build script.
Kotlin
1.5.0
for
KMM
Kotlin 1.5.0 introduces a number of improvements and features that are helpful for KMM:
Simplified test dependency selection for each platform. Now you can use the kotlin-test dependency to add
dependencies for testing in the commonTest source set. The Gradle plugin will infer the corresponding platform
dependencies for each test source set:
iOS source sets use Kotlin/Native, which has everything built in, so they do not require any additional artifacts.
You can also use the kotlin-test dependency in any shared or platform-specific source set. Learn more about setting
dependencies on test libraries.
New API for getting a char’s Unicode category. A variety of new character-related functions are available on all
platforms and in the common code. They include several functions for checking whether a char is a letter or a digit,
like Char.isLetterOrDigit(), as well as functions for checking the case of a char, like Char.isUpperCase(). The property
Char.category and the enum class CharCategory are available, as well.
Learn more about this new API.
Improved Kotlin/Native performance and stability. Kotlin/Native is receiving a set of performance improvements that
speed up both compilation and execution.
Learn more about the Kotlin/Native improvements.
Kotlin
1.4.30
for
KMM
Kotlin 1.4.30 introduces a number of improvements that are helpful for KMM:
Improved compilation time for an iOS simulator. Recompiling binaries for the iOS simulator after making changes in the
code now requires much less time. You can see the most significant improvements when re-running unit tests or
165
applications on the iOS simulator. 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).
These optimizations affect other scenarios as well.
Support for the new watchosX64 target in Kotlin/Native. This target makes it possible to run the simulator on 64-bit
architecture.
Kotlin
1.4.20
for
KMM
Kotlin 1.4.20 introduces a number of features, improvements, and bug fixes that are helpful for KMM:
Ability to add dependencies on libraries from a custom spec repository, Git repository, or archive, as well as on
libraries with custom cinterop options.
Learn more about adding CocoaPods dependencies and these improvements.
Escape analysis for Kotlin/Native. A prototype of a new mechanism that gives a 10% iOS runtime performance
improvement by allocating certain objects on the stack instead of the heap.
Opt-in wrapping of Objective-C exceptions in runtime to avoid crashes. Learn how to opt in.
Updated structure of multiplatform library publications. The library root publication, which stands for the whole library,
now includes metadata artifacts. These were published separately in earlier Kotlin versions.
For compatibility, both multiplatform library authors and users must update to Kotlin 1.4.20. Learn more about
publishing a multiplatform library.
Deprecation of the Kotlin Android Extensions plugin. The Parcelable implementation generator has been moved to a
separate kotlin-parcelize plugin.
What's
new
in
Kotlin
plugin
2021.2
Enjoy improved performance, a better coroutines debugging experience, WSL 2 and Run Targets support, and more
improvements for Kotlin in IntelliJ IDEA 2021.2:
Performance improvements
166
Remote development support
Performance
improvements
IntelliJ IDEA 2021.2 brings some major performance improvements for Kotlin:
Faster test files analysis. Now you can run tests before code analysis finishes. The Run test icon appears in the gutter
as soon as you open the file and you can run your test immediately.
Run and debug your code before the IDE finishes indexing. The indexing process in the IDE and running code are now
autonomous from each other. You can run or debug the project right away without waiting for the IDE to finish its work.
Improved speed of rename refactoring. Rename refactoring for particular cases, like for fields with common names
name or id, became faster. Check out this YouTrack issue for more details.
Shared indexes for new Spring Boot projects. Shared indexes help you to prevent situations where you open a project
and need to wait for indexing to finish. In the previous version of IntelliJ IDEA you can download the JDK shared
indexes that save time during every project import. Now shared indexes are available for Spring Boot projects. Learn
more about how to download and use shared indexes in the IntelliJ IDEA documentation.
Better
debugging
experience
IntelliJ IDEA 2021.2 brings useful improvements and updates to the coroutine agent in the debugger:
Now you can evaluate suspend function calls during the debugging process. You can put a breakpoint and evaluate
the suspend function:
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
167
val sum = deferred.await() + fastFun()
println(sum)
}
Look through these YouTrack tickets for more details: KT-27974, KT-31701.
Previously, when local variables were not used after passing a suspension point, you couldn’t see their values in
the Local Variable table. This helped avoid memory leaks, but as a side effect such variables disappeared in the
Variables view of the Debugger tool window.
Now you can see the values of such variables for common cases. IntelliJ IDEA also handles other specific cases
properly and notifies you when it is impossible to obtain the value.
For example, when you debug through the following code, the debugger shows the message that the x1, x2, x3
variables have been optimized out:
import kotlinx.coroutines.runBlocking
}
fun main() = runBlocking {
// Set a breakpoint here:
val x1 = 1
println(x1)
foo()
val x2 = 2
println(x2)
foo()
val x3 = 3
println(x3)
foo()
println()
}
Check out these YouTrack issues for more details: KTIJ-18499, KTIJ-18630.
The coroutines agent is now available for Java, Maven, and Spring run configurations with a dependency on
kotlinx.coroutines.
Remote
development
support
Some popular remote-development scenarios are now available for Kotlin projects: WSL 2 (Windows Subsystem for
Linux) support and the Run Targets feature.
Run, debug, and test your code in different remote environments without leaving the IDE.
168
Kotlin
plugin
in
the
IntelliJ
IDEA
repository
The Kotlin plugin code has been moved to the IntelliJ IDEA repository. That means that every stable IDE release improves
your Kotlin experience and brings you more debugging, refactoring, and IDE-related features. To contribute to the Kotlin
plugin, clone the IntelliJ IDEA repository.
Since the Kotlin plugin and Kotlin have separate release cycles, this creates some limitations that are important to
emphasize:
The EAP version of Kotlin works only with the stable version of the IDE. That means that you can't install the Kotlin EAP
version to the EAP IDEA release.
The Kotlin plugin is based on the previous stable version of the Kotlin compiler. You can still update the Kotlin version
in your project, but some IDE-related features might not be available. We are working on stabilizing the process so that
the next versions of the plugin will be based on the latest stable version of the compiler.
Learn more about the EAP for Kotlin and IntelliJ IDEA.
Other
IDE
improvements
IntelliJ IDEA 2021.2 also brings more IDE features that improve the Kotlin experience:
Automatic ML code completion. Kotlin code completion works based on a machine learning mechanism by default.
Code suggestions are prioritized more carefully as IntelliJ IDEA considers the choices of thousands of real users in
similar situations. You can configure ML-assisted completion in Preferences/Settings | Editor | Code Completion.
Clickable inlay hints. Now you can click the type in the inlay hint and look through the declaration of the type, including
generics types. Just hold Cmd + click the type in the hint:
You can customize the inlay hints’ appearance in Preferences | Editor | Inlay hints | Kotlin.
Package Search integration. Package Search now works with build.gradle.kts files. This feature allows you to upgrade,
downgrade, and remove existing dependencies. You can use it to find new dependencies and add them automatically.
Package Search will add the required repositories to your build script if they’re missing.
169
Advanced settings. There is a new node Advanced Settings in the Preferences | Settings window. It contains some
use-case-specific options conveniently grouped by the IDE tool. For example, you can add a left margin in Distraction-
free mode, or set the maximum number of recent projects which are displayed in the File | Open Recent menu.
Quick access to Eclipse projects. IntelliJ IDEA detects existing Eclipse projects automatically and adds them to the
Welcome screen. To try this feature, select Open existing Eclipse projects on your first IDE launch.
See the What’s new in IntelliJ IDEA 2021.2 blog post to learn more about the platform enhancements.
What's
new
in
Kotlin
plugin
2021.1
This release aims to increase productivity and improve the development experience. Version 2021.1 of the Kotlin plugin
introduces the following major updates:
Performance improvements
You can also learn about new features in this blog post.
Performance
improvements
With 2021.1, the Kotlin plugin has received a number of performance improvements that speed up the development
process.
Faster syntax and error highlighting. The code highlighting API has been reworked, so you now get all the necessary
diagnostic information faster. See the benchmark results and share your feedback in this YouTrack issue.
Code completion speed improvements. The Kotlin plugin provides faster code completion. Check out this YouTrack
issue to see the benchmark results.
This version also introduces other improvements for code completion, particularly code completion for type
arguments.
Improved IDE responsiveness. We’ve fixed numerous issues based on your feedback, and we’ve improved the
overall stability of the plugin, as well as IDE responsiveness.
170
Evaluation
of
custom
getters
during
debugging
This release expands the capabilities of the debugger. One of the most notable features is the ability to display Kotlin
properties without a backing field in the Variables view.
Previously, during a debug session, you could only see the properties without a custom getter and those with a backing
field. Properties with a custom getter didn't appear because they are represented as regular methods on the JVM.
Starting with version 2021.1 of the Kotlin plugin, you can see all such properties and evaluate them on demand by clicking
on `get() near the property name.
For example, when debugging of the following code, you can execute the get() method to see the value:
fun main() {
val version = LanguageVersion(1, 4)
}
In the Debug window, you can see the values of the properties:
Variables view
Improved
Change
Signature
refactoring
This release contains changes to the Change Signature refactoring. We’ve fixed more than 40 issues to make the
refactoring process better.
171
Here are some of the important improvements:
Fixes to inheritance refactoring, including cross-language refactoring. See the YouTrack issue for details.* Improved
UX of properties processing.
Addition of a way to declare the default parameter value. See the YouTrack issue for details.
Code
completion
for
type
arguments
From now on, code completion will offer functions and properties after generic functions and will restore type arguments
where needed. When you select such a function from the list, the IDE adds the correct type argument to the preceding
code.
fun typeArgumentsAtCodeCompletion() {
// Function definition from stdlib:
// public fun <T> emptyList(): List<T>
val list: List<String> = emptyList() // T is inferred from the context (explicit variable
type)
}
To reverse this list, start typing the name of the reversed() function:
After you apply the code completion suggestion, the IDE automatically adds the <String> type:
fun typeArgumentsAtCodeCompletion() {
// Function definition from stdlib:
// public fun <T> emptyList(): List<T>
172
}
The type argument for emptyList() is required to evaluate the expression type. Otherwise, the code will not compile.
UML
diagrams
for
Kotlin
classes
With this release, you can test Kotlin code visualization via UML Class diagrams. To build a diagram, select Diagrams |
Show Diagram... | Kotlin Classes in the Project View.
Currently, the diagrams only show inheritance and nesting relationships. All other more detailed association connections,
like aggregation, composition, dependency, and others will be available in the future releases.
Other
platform
enhancements
Since the plugin and the platform have been moved to the same codebase and now ship simultaneously, this release also
brings the following features that improve the Kotlin experience:
You can now launch the basic memory profiler that was announced in the IntelliJ IDEA 2020.3 release via Gradle run.
173
To improve the experience of working with coroutines, this release provides better thread-blocking call detection. The
inspection now correctly warns you about inappropriate blocking method calls.
We’ve fixed some of the language injection issues that received the most votes. Language injection now works
correctly for the following cases:
When you use triple quotes to inject multi-line strings and add the .trimIndent(), or .trimMargin() functions at the end.
See the YouTrack issue for details.
When you concatenate strings. See the YouTrack issue for details.
What's
new
in
Kotlin
plugin
2020.3
This plugin release introduces the following features:
EditorConfig support
New
types
of
inline
refactorings
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 defined 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
Structural search and replace (SSR) actions are now available for Kotlin. The SSR feature allows you to find 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 file and select one of the templates or create your
own.
EditorConfig
support
From 2020.3, the Kotlin API has the full support of .editorconfig files for managing code style on a directory level.
174
Project
templates
for
Jetpack
Compose
for
Desktop
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:
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 configuration files 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.
Basic
syntax
This is a collection of basic syntax elements with examples. At the end of every section, you'll find a link to a detailed
description of the related topic.
You can also learn all the Kotlin essentials with the free Kotlin Basics track on JetBrains Academy.
175
Package
definition
and
imports
Package specification should be at the top of the source file.
package my.demo
import kotlin.text.*
// ...
It is not required to match directories and packages: source files can be placed arbitrarily in the file system.
See Packages.
Program
entry
point
An entry point of a Kotlin application is the main function.
fun main() {
println("Hello world!")
}
Print
to
the
standard
output
print prints its argument to the standard output.
fun main() {
//sampleStart
print("Hello ")
print("world!")
//sampleEnd
}
println prints its arguments and adds a line break, so that the next thing you print appears on the next line.
fun main() {
//sampleStart
println("Hello world!")
println(42)
//sampleEnd
}
176
Functions
A function with two Int parameters and 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))
}
//sampleStart
fun sum(a: Int, b: Int) = a + b
//sampleEnd
fun main() {
println("sum of 19 and 23 is ${sum(19, 23)}")
}
//sampleStart
fun printSum(a: Int, b: Int): Unit {
println("sum of $a and $b is ${a + b}")
}
//sampleEnd
fun main() {
printSum(-1, 8)
}
//sampleStart
fun printSum(a: Int, b: Int) {
println("sum of $a and $b is ${a + b}")
}
//sampleEnd
fun main() {
printSum(-1, 8)
}
177
See Functions.
Variables
Read-only local variables are defined 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")
}
fun main() {
//sampleStart
var x = 5 // `Int` type is inferred
x += 1
//sampleEnd
println("x = $x")
}
//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")
}
Creating
classes
and
instances
To define a class, use the class keyword.
178
class Shape
The default constructor with parameters listed in the class declaration is available automatically.
Inheritance between classes is declared by a colon (:). Classes are final by default; to make a class inheritable, mark it as
open.
Comments
Just like most modern languages, Kotlin supports single-line (or end-of-line) and multi-line (block) comments.
See Documenting Kotlin Code for information on the documentation comment syntax.
179
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)}")
}
//sampleStart
fun maxOf(a: Int, b: Int) = if (a > b) a else b
//sampleEnd
fun main() {
println("max of 0 and 42 is ${maxOf(0, 42)}")
}
See if -expressions.
for
loop
fun main() {
//sampleStart
180
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
//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"))
181
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
}
fun main() {
//sampleStart
val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
println("-1 is out of range")
}
if (list.size !in list.indices) {
println("list size is out of valid list indices range, too")
}
//sampleEnd
}
fun main() {
//sampleStart
for (x in 1..5) {
print(x)
}
//sampleEnd
}
Or over a progression.
fun main() {
//sampleStart
for (x in 1..10 step 2) {
182
print(x)
}
println()
for (x in 9 downTo 0 step 3) {
print(x)
}
//sampleEnd
}
Collections
Iterate over a collection.
fun main() {
val items = listOf("apple", "banana", "kiwifruit")
//sampleStart
for (item in items) {
println(item)
}
//sampleEnd
}
fun main() {
val items = setOf("apple", "banana", "kiwifruit")
//sampleStart
when {
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}
//sampleEnd
}
fun main() {
//sampleStart
val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
fruits
.filter { it.startsWith("a") }
.sortedBy { it }
.map { it.uppercase() }
.forEach { println(it) }
//sampleEnd
}
183
Nullable
values
and
null
checks
A reference must be explicitly marked as nullable when null value is possible. Nullable type names have ? at the end.
//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
184
println("Wrong number format in arg2: '$arg2'")
return
}
fun main() {
printProduct("6", "7")
printProduct("a", "7")
printProduct("99", "b")
}
See Null-safety.
Type
checks
and
automatic
casts
The is operator checks if an expression is an instance of a type. If an immutable local variable or property is checked for a
specific type, there's no need to cast it explicitly:
//sampleStart
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// `obj` is automatically cast to `String` in this branch
return obj.length
}
fun main() {
fun printLength(obj: Any) {
println("Getting the length of '$obj'. Result: ${getStringLength(obj) ?: "Error: The
object is not a string"} ")
}
printLength("Incomprehensibilities")
printLength(1000)
printLength(listOf(Any()))
}
or
//sampleStart
fun getStringLength(obj: Any): Int? {
if (obj !is String) return null
185
fun main() {
fun printLength(obj: Any) {
println("Getting the length of '$obj'. Result: ${getStringLength(obj) ?: "Error: The
object is not a string"} ")
}
printLength("Incomprehensibilities")
printLength(1000)
printLength(listOf(Any()))
}
or even
//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("Getting the length of '$obj'. Result: ${getStringLength(obj) ?: "Error: The
object is not a string"} ")
}
printLength("Incomprehensibilities")
printLength("")
printLength(1000)
}
Idioms
A collection of random and frequently used idioms in Kotlin. If you have a favorite idiom, contribute it by sending a pull
request.
Create
DTOs
(POJOs/POCOs)
equals()
186
hashCode()
toString()
copy()
Default
values
for
function
parameters
Filter
a
list
Check
the
presence
of
an
element
in
a
collection
String
interpolation
println("Name $name")
Instance
checks
when (x) {
187
is Foo -> ...
is Bar -> ...
else -> ...
}
Read-only
list
Read-only
map
Access
a
map
entry
println(map["key"])
map["key"] = value
Traverse
a
map
or
a
list
of
pairs
Iterate
over
a
range
Lazy
property
188
val p: String by lazy {
// compute the string
}
Extension
functions
Create
a
singleton
object Resource {
val name = "Name"
}
Instantiate
an
abstract
class
fun main() {
val myObject = object : MyAbstractClass() {
override fun doSomething() {
// ...
}
If-not-null
shorthand
189
If-not-null-else
shorthand
Execute
a
statement
if
null
Get
first
item
of
a
possibly
empty
collection
Execute
if
not
null
value?.let {
... // execute this block if not null
}
Map
nullable
value
if
not
null
Return
on
when
statement
190
}
}
try-catch
expression
fun test() {
val result = try {
count()
} catch (e: ArithmeticException) {
throw IllegalStateException(e)
}
if
expression
Builder-style
usage
of
methods
that
return
Unit
Single-expression
functions
fun theAnswer() = 42
This is equivalent to
191
This can be effectively combined with other idioms, leading to shorter code. For example, with the when expression:
Call
multiple
methods
on
an
object
instance
(with)
class Turtle {
fun penDown()
fun penUp()
fun turn(degrees: Double)
fun forward(pixels: Double)
}
Configure
properties
of
an
object
(apply)
This is useful for configuring properties that aren't present in the object constructor.
Java
7's
try-with-resources
192
Generic
function
that
requires
the
generic
type
information
Nullable
Boolean
Swap
two
variables
var a = 1
var b = 2
a = b.also { b = a }
Mark
code
as
incomplete
(TODO)
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.
What’s
next?
Learn how to perform typical tasks with strings in Java and Kotlin.
193
Coding
conventions
Commonly known and easy-to-follow coding conventions are vital for any programming language. Here we provide
guidelines on the code style and code organization for projects that use Kotlin.
Configure
style
in
IDE
Two most popular IDEs for Kotlin - IntelliJ IDEA and Android Studio provide powerful support for code styling. You can
configure them to automatically format your code in consistence with the given code style.
Apply
the
style
guide
1. Go to Settings | Editor | Code Style | Kotlin.
Verify
that
your
code
follows
the
style
guide
1. Go to Settings | Editor | Inspections | Kotlin.
3. Switch on 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.
Source
code
organization
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, files
with the org.example.kotlin package should be placed directly under the source root, and files in
org.example.kotlin.network.socket should be in the network/socket subdirectory of the source root.
On JVM: In projects where Kotlin is used together with Java, Kotlin source files should reside in the same
source root as the Java source files, and follow the same directory structure: each file should be stored in the
directory corresponding to each package statement.
Source
file
names
194
If a Kotlin file 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 file contains multiple classes, or only top-level declarations,
choose a name describing what the file contains, and name the file accordingly. Use upper camel case with an uppercase
first letter (also known as Pascal case), for example, ProcessDeclarations.kt.
The name of the file should describe what the code in the file does. Therefore, you should avoid using meaningless words
such as Util in file names.
Source
file
organization
Placing multiple declarations (classes, top-level functions or properties) in the same Kotlin source file is encouraged as
long as these declarations are closely related to each other semantically, and the file size remains reasonable (not
exceeding a few hundred lines).
In particular, when defining extension functions for a class which are relevant for all clients of this class, put them in the
same file with the class itself. When defining extension functions that make sense only for a specific client, put them next
to the code of that client. Avoid creating files just to hold all extensions of some class.
Class
layout
The contents of a class should go in the following order:
2. Secondary constructors
3. Method declarations
4. Companion object
Do not sort the method declarations alphabetically or by visibility, and do not separate regular methods from extension
methods. Instead, put related stuff 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 stuff first, 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.
Interface
implementation
layout
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
Always put overloads next to each other in a class.
Naming
rules
195
Package and class naming rules in Kotlin are quite simple:
Names of packages are always lowercase 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 just concatenate them together or
use camel case (org.example.myProject).
Names of classes and objects start with an uppercase letter and use camel case:
Function
names
Names of functions, properties and local variables start with a lowercase letter and use camel case and no underscores:
Exception: factory functions used to create instances of classes can have the same name as the abstract return type:
Names
for
test
methods
In tests (and only in tests), you can 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`() { /*...*/ }
@Test fun ensureEverythingWorks_onAndroid() { /*...*/ }
}
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 (screaming snake case) names:
196
Names of top-level or object properties which hold objects with behavior or mutable data should use camel case names:
Names of properties holding references to singleton objects can use the same naming style as object declarations:
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.
Names
for
backing
properties
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 prefix for the name of the private property:
class C {
private val _elementList = mutableListOf<Element>()
Choose
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) in names.
When using an acronym as part of a declaration name, capitalize it if it consists of two letters ( IOStream); capitalize only
the first letter if it is longer (XmlFormatter, HttpInputStream).
Formatting
Indentation
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.
197
if (elements != null) {
for (element in elements) {
// ...
}
}
In Kotlin, semicolons are optional, and therefore line breaks are significant. The language design assumes Java-
style braces, and you may encounter surprising behavior if you try to use a different 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 flow 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.
fun bar() {
foo(1)
}
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 identifier to a name with a different length should
not affect the formatting of either the declaration or any of the usages.
Colon
Put a space before : in the following cases:
198
when delegating to a superclass constructor or a different constructor of the same class
Don't put a space before : when it separates a declaration and its type.
Class
headers
Classes with a few primary constructor parameters can be written in a single line:
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 you use inheritance, the superclass constructor call
or the 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 first and then each interface should be located
in a different line:
class Person(
id: Int,
name: String,
surname: String
) : Human(id, name),
KotlinMaker { /*...*/ }
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 {
199
fun foo() { /*...*/ }
}
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() { /*...*/ }
}
Use regular indent (four spaces) for constructor parameters. This ensures that properties declared in the primary
constructor have the same indentation as properties declared in the body of a class.
Modifiers
order
If a declaration has multiple modifiers, always put them in the following order:
@Named("Foo")
private val foo: Foo
Unless you're working on a library, omit redundant modifiers (for example, public).
Annotations
Place annotations on separate lines before the declaration to which they are attached, and with the same indentation:
200
@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude
@JsonExclude @JvmField
var x: String
A single annotation without arguments may be placed on the same line as the corresponding declaration:
File
annotations
File annotations are placed after the file comment (if any), before the package statement, and are separated from package
with a blank line (to emphasize the fact that they target the file and not the package).
package foo.bar
Functions
If the function signature doesn't fit on a single line, use the following syntax:
fun longMethodName(
argument: ArgumentType = defaultValue,
argument2: AnotherArgumentType,
): ReturnType {
// body
}
Use regular indent (four spaces) for function parameters. It helps ensure consistency with constructor parameters.
Prefer using an expression body for functions with the body consisting of a single expression.
Expression
bodies
If the function has an expression body whose first line doesn't fit on the same line as the declaration, put the = sign on the
201
first line and indent the expression body by four spaces.
Properties
For very simple read-only properties, consider one-line formatting:
For more complex properties, always put get and set keywords on separate lines:
For properties with an initializer, if the initializer is long, add a line break after the = sign and indent the initializer by four
spaces:
Control
flow
statements
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)
}
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
202
} 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:
Put short branches on the same line as the condition, without braces.
when (foo) {
true -> bar() // good
false -> { baz() } // bad
}
Method
calls
In long argument lists, put a line break after the opening parenthesis. Indent arguments by four spaces. Group multiple
closely related arguments on the same line.
drawSquare(
x = 10, y = 10,
width = 100, height = 100,
fill = true
)
Put spaces around the = sign separating the argument name and value.
Wrap
chained
calls
When wrapping chained calls, put the . character or the ?. operator on the next line, with a single indent:
The first 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.
203
Lambdas
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, pass it 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 first line, followed by the arrow and the
newline:
If the parameter list is too long to fit 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
)
It makes version-control diffs 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 simplifies code generation, for example, for object initializers. The last element can also have a comma.
204
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 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
205
) {}
fun print(
vararg quantity: Int,
description: String, // trailing comma
) {}
Indexing suffix
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
]
Parameters in lambdas
fun main() {
val x = {
x: Comparable<Number>,
y: Iterable<Number>, // trailing comma
->
println("1")
}
println(x)
}
when entry
206
}
Type arguments
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
val cars = listOf<Car>()
fun printMeanValue() {
var meanValue: Int = 0
for ((
_,
_,
year, // trailing comma
) in cars) {
meanValue += year
207
}
println(meanValue/cars.size)
}
printMeanValue()
Documentation
comments
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 fit into the flow of the main text.
/**
* 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) { /*...*/ }
Avoid
redundant
constructs
In general, if a certain syntactic construction in Kotlin is optional and highlighted by the IDE as redundant, you should omit
it in your code. Do not leave unnecessary syntactic elements in code just "for clarity".
Unit
return
type
If a function returns Unit, the return type should be omitted:
208
fun foo() { // ": Unit" is omitted here
Semicolons
Omit semicolons whenever possible.
String
templates
Don't use curly braces when inserting a simple variable into a string template. Use curly braces only for longer
expressions.
Idiomatic
use
of
language
features
Immutability
Prefer using immutable data to mutable. Always declare local variables and properties as val rather than var if they are not
modified 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>) { ... }
Default
parameter
values
Prefer declaring functions with default parameter values to declaring overloaded functions.
// Bad
fun foo() = foo("a")
fun foo(a: String) { /*...*/ }
209
// 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 defining a
type alias for it:
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, always declare parameters 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
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.
Conditional
statements
Prefer using the expression form of try, if, and when.
return when(x) {
0 -> "zero"
else -> "nonzero"
}
210
if (x)
return foo()
else
return bar()
when(x) {
0 -> return "zero"
else -> return "nonzero"
}
if
versus
when
Prefer using if for binary conditions instead of when. For example, use this syntax with if:
when (x) {
null -> // ...
else -> // ...
}
Nullable
Boolean
values
in
conditions
If you need to use a nullable Boolean in a conditional statement, use if (value == true) or if (value == false) checks.
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
Use the until function to loop over an open range:
211
Strings
Prefer string templates to string concatenation.
Prefer multiline strings to 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:
fun main() {
//sampleStart
println("""
Not
trimmed
text
"""
)
println("""
Trimmed
text
""".trimIndent()
)
println()
println(a)
//sampleEnd
}
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.
returns the same result over invocations if the object state hasn't changed
Extension
functions
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
212
functions as much as it makes sense. As necessary, use local extension functions, member extension functions, or top-
level extension functions with private visibility.
Infix
functions
Declare a function as infix only when it works on two objects which play a similar role. Good examples: and, to, zip. Bad
example: add.
Factory
functions
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.
If you have an object with multiple overloaded constructors that don't call different 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:
Any property (package-level or class-level) initialized 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)
}
213
Scope
functions
apply/with/run/also/let
Kotlin provides a set 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.
Coding
conventions
for
libraries
When writing libraries, it's recommended to follow an additional set of rules to ensure API stability:
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)
Basic
types
In Kotlin, everything is an object in the sense that we can call member functions and properties on any variable. Some
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, booleans, characters, strings, and arrays.
Numbers
Integer
types
Kotlin provides a set of built-in types that represent numbers.
For integer numbers, there are four types with different 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 suffix L to the value.
214
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1
Floating-point
types
For real numbers, Kotlin provides floating-point types Float and Double. According to the IEEE 754 standard, floating
point types differ by their decimal place, that is, how many decimal digits they can store. Float reflects the IEEE 754 single
precision, while Double provides double precision.
Float 32 24 8 6-7
Double 64 53 11 15-16
You can initialize Double and Float variables with numbers having a fractional part. It's separated from the integer part by
a period (.) For variables initialized with fractional numbers, the compiler infers the Double type.
To explicitly specify the Float type for a value, add the suffix f or F. If such a value contains more than 6-7 decimal digits,
it will be rounded.
Note that unlike some other languages, there are no implicit widening conversions for numbers in Kotlin. For example, a
function with a Double parameter can be called only on Double values, but not Float, Int, or other numeric values.
fun main() {
fun printDouble(d: Double) { print(d) }
val i = 1
val d = 1.0
val f = 1.0f
printDouble(d)
// printDouble(i) // Error: Type mismatch
// printDouble(f) // Error: Type mismatch
}
215
Literal
constants
There are the following kinds of literal constants for integral values:
Decimals: 123
Longs are tagged by a capital L: 123L
Hexadecimals: 0x0F
Binaries: 0b00001011
Numbers
representation
on
the
JVM
On the JVM platform, numbers are stored as primitive types: int, double, and so on. Exceptions are cases when you
create a nullable number reference such as Int? or use generics. In these cases numbers are boxed in Java classes
Integer, Double, and so on.
Note that nullable references to the same number can be different objects:
fun main() {
//sampleStart
val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a
val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedA === anotherBoxedA) // true
println(boxedB === anotherBoxedB) // false
//sampleEnd
}
216
All nullable references to a are actually the same object because of the memory optimization that JVM applies to Integer s
between -128 and 127. It doesn't apply to the b references, so they are different objects.
fun main() {
//sampleStart
val b: Int = 10000
println(b == b) // Prints 'true'
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedB == anotherBoxedB) // Prints 'true'
//sampleEnd
}
Explicit
conversions
Due to different representations, smaller types are not subtypes of bigger ones. If they were, we would have troubles of
the following sort:
As a consequence, smaller types are NOT implicitly converted to bigger types. This means that assigning a value of type
Byte to an Int variable requires an explicit conversion.
fun main() {
//sampleStart
val b: Byte = 1 // OK, literals are checked statically
// val i: Int = b // ERROR
val i1: Int = b.toInt()
//sampleEnd
}
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
217
toDouble(): Double
toChar(): Char
In many cases, there is no need in explicit conversions because the type is inferred from the context, and arithmetical
operations are overloaded for appropriate conversions, for example:
Operations
Kotlin supports the standard set of arithmetical operations over numbers: +, -, *, /, %. They are declared as members of
appropriate classes.
fun main() {
//sampleStart
println(1 + 2)
println(2_500_000_000L - 1L)
println(3.14 * 2.71)
println(10.0 / 3)
//sampleEnd
}
You can also override these operators for custom classes. See Operator overloading for details.
Division of integers
Division between integers numbers always returns an integer number. Any fractional part is discarded.
fun main() {
//sampleStart
val x = 5 / 2
//println(x == 2.5) // ERROR: Operator '==' cannot be applied to 'Int' and 'Double'
println(x == 2)
//sampleEnd
}
fun main() {
//sampleStart
val x = 5L / 2
println(x == 2L)
//sampleEnd
}
To return a floating-point type, explicitly convert one of the arguments to a floating-point type.
fun main() {
218
//sampleStart
val x = 5 / 2.toDouble()
println(x == 2.5)
//sampleEnd
}
Bitwise operations
Kotlin provides a set of bitwise operations on integer numbers. They operate on the binary level directly with bits of the
numbers' representation. Bitwise operations are represented by functions that can be called in infix form. They can be
applied only to Int and Long.
Floating-point
numbers
comparison
The operations on floating-point numbers discussed in this section are:
When the operands a and b are statically known to be Float or Double or their nullable counterparts (the type is declared
or inferred or is a result of a smart cast), the operations on the numbers and the range that they form follow the IEEE 754
Standard for Floating-Point Arithmetic.
However, to support generic use cases and provide total ordering, when the operands are not statically typed as floating
point numbers (e.g. Any, Comparable<...>, a type parameter), the operations use the equals and compareTo
implementations for Float and Double, which disagree with the standard, so that:
219
-0.0 is considered less than 0.0
Unsigned
integers
In addition to integer types, Kotlin provides the following types for unsigned integer numbers:
Changing type from unsigned type to signed counterpart (and vice versa) is a binary incompatible change.
Unsigned arrays and operations on them are in Beta. They can be changed incompatibly at any time. Opt-in is
required (see the details below).
Same as for primitives, each of unsigned type has corresponding type that represents arrays of that type:
Same as for signed integer arrays, they provide similar API to Array class without boxing overhead.
When you use unsigned arrays, you'll get a warning that indicates that this feature is not stable yet. To remove the
warning, opt in using the @ExperimentalUnsignedTypes annotation. It's up to you to decide if your clients have to
explicitly opt-in into usage of your API, but keep in mind that unsigned array are not a stable feature, so API which uses
them can be broken by changes in the language. Learn more about opt-in requirements.
Ranges and progressions are supported for UInt and ULong by classes UIntRange, UIntProgression, ULongRange, and
ULongProgression. Together with the unsigned integer types, these classes are stable.
Literals
To make unsigned integers easier to use, Kotlin provides an ability to tag an integer literal with a suffix indicating a
220
specific unsigned type (similarly to Float or Long):
u and U tag unsigned literals. The exact type is determined based on the expected type. If no expected type is
provided, compiler will use UInt or ULong depending on the size of literal.
val a = 1UL // ULong, even though no expected type provided and constant fits into UInt
Further discussion
See language proposal for unsigned types for technical details and further discussion.
Booleans
The type Boolean represents boolean objects that can have two values: true and false.
Boolean has a nullable counterpart Boolean? that also has the null value.
fun main() {
//sampleStart
val myTrue: Boolean = true
val myFalse: Boolean = false
val boolNull: Boolean? = null
println(myTrue || myFalse)
println(myTrue && myFalse)
println(!myTrue)
//sampleEnd
}
221
On JVM: nullable references to boolean objects are boxed similarly to numbers.
Characters
Characters are represented by the type Char. Character literals go in single quotes: '1'.
Special characters start from an escaping backslash \. The following escape sequences are supported: \t, \b, \n, \r, \', \",
\\ and \$.
To encode any other character, use the Unicode escape sequence syntax: '\uFF00'.
fun main() {
//sampleStart
val aChar: Char = 'a'
println(aChar)
println('\n') //prints an extra newline character
println('\uFF00')
//sampleEnd
}
If a value of character variable is a digit, you can explicitly convert it to an Int number using the digitToInt() function.
On JVM: Like numbers, characters are boxed when a nullable reference is needed. Identity is not preserved by
the boxing operation.
Strings
Strings in Kotlin are represented by the type String. Generally, a string value is a sequence of characters in double quotes
(").
Elements of a string are characters that you can access via the indexing operation: s[i]. You can iterate over these
characters with a for loop:
fun main() {
val str = "abcd"
//sampleStart
for (c in str) {
println(c)
}
//sampleEnd
}
222
Strings are immutable. Once you initialize a string, you can't change its value or assign a new value to it. All operations
that transform strings return their results in a new String object, leaving the original string unchanged.
fun main() {
//sampleStart
val str = "abcd"
println(str.uppercase()) // Create and print a new String object
println(str) // the original string remains the same
//sampleEnd
}
To concatenate strings, use the + operator. This also works for concatenating strings with values of other types, as long
as the first element in the expression is a string:
fun main() {
//sampleStart
val s = "abc" + 1
println(s + "def")
//sampleEnd
}
Note that in most cases using string templates or raw strings is preferable to string concatenation.
String
literals
Kotlin has two types of string literals:
Escaping is done in the conventional way, with a backslash (\). See Characters above for the list of supported escape
sequences.
A raw string is delimited by a triple quote ("""), contains no escaping and can contain newlines and any other characters:
To remove leading whitespace from raw strings, use the trimMargin() function:
223
|Teach me and I remember.
|Involve me and I learn.
|(Benjamin Franklin)
""".trimMargin()
By default, | is used as margin prefix, but you can choose another character and pass it as a parameter, like
trimMargin(">").
String
templates
String literals may contain template expressions - pieces of code that are evaluated and whose results are concatenated
into the string. A template expression starts with a dollar sign ($) and consists of either a name:
fun main() {
//sampleStart
val i = 10
println("i = $i") // prints "i = 10"
//sampleEnd
}
fun main() {
//sampleStart
val s = "abc"
println("$s.length is ${s.length}") // prints "abc.length is 3"
//sampleEnd
}
You can use templates both in raw and escaped strings. To insert the $ character in a raw string (which doesn't support
backslash escaping) before any symbol, which is allowed as a beginning of an identifier, use the following syntax:
Arrays
Arrays in Kotlin are represented by the Array class. It has get and set functions that turn into [] by operator overloading
conventions, and the size property, along with other useful member functions:
224
}
To create an array, use the function arrayOf() and pass the item values to it, so that arrayOf(1, 2, 3) creates an array [1, 2,
3]. Alternatively, the arrayOfNulls() function can be used to create an array of a given size filled with null elements.
Another option is to use the Array constructor that takes the array size and the function that returns values of array
elements given its index:
fun main() {
//sampleStart
// Creates an Array<String> with values ["0", "1", "4", "9", "16"]
val asc = Array(5) { i -> (i * i).toString() }
asc.forEach { println(it) }
//sampleEnd
}
As we said above, the [] operation stands for calls to member functions get() and set().
Arrays in Kotlin are invariant. This means that Kotlin does not let us assign an Array<String> to an Array<Any>, which
prevents a possible runtime failure (but you can use Array<out Any>, see Type Projections).
Primitive
type
arrays
Kotlin also has classes that represent arrays of primitive types without boxing overhead: ByteArray, ShortArray, IntArray,
and so on. These classes have no inheritance relation to the Array class, but they have the same set of methods and
properties. Each of them also has a corresponding factory function:
Type
checks
and
casts
is
and
!is
operators
Use the is operator or its negated form !is to perform a runtime check that identifies whether an object conforms to a
225
given type:
if (obj is String) {
print(obj.length)
}
Smart
casts
In most cases, you don't need to use explicit cast operators in Kotlin because the compiler tracks the is -checks and
explicit casts for immutable values and inserts (safe) casts automatically when necessary:
The compiler is smart enough to know that a cast is safe if a negative check leads to a return:
Smart casts work for when expressions and while loops as well:
when (x) {
is Int -> print(x + 1)
is String -> print(x.length + 1)
is IntArray -> print(x.sum())
}
Note that smart casts work only when the compiler can guarantee that the variable won't change between the check and
the usage. More specifically, smart casts can be used under the following conditions:
226
val local variables - always, with the exception of local delegated properties.
val properties - if the property is private or internal or if the check is performed in the same module where the property
is declared. Smart casts cannot be used on open properties or properties that have custom getters.
var local variables - if the variable is not modified between the check and the usage, is not captured in a lambda that
modifies it, and is not a local delegated property.
var properties - never, because the variable can be modified at any time by other code.
"Unsafe"
cast
operator
Usually, the cast operator throws an exception if the cast isn't possible. And so, it's called unsafe. The unsafe cast in
Kotlin is done by the infix operator as.
Note that null cannot be cast to String, as this type is not nullable. If y is null, the code above throws an exception. To
make code like this correct for null values, use the nullable type on the right-hand side of the cast:
"Safe"
(nullable)
cast
operator
To avoid exceptions, use the safe cast operator as?, which returns null on failure.
Note that despite the fact that the right-hand side of as? is a non-null type String, the result of the cast is nullable.
Type
erasure
and
generic
type
checks
Kotlin ensures type safety for operations involving generics at compile time, while, at runtime, instances of generic types
don't hold information about their actual type arguments. For example, List<Foo> is erased to just List<*>. In general,
there is no way to check whether an instance belongs to a generic type with certain type arguments at runtime.
Because of that, the compiler prohibits is -checks that cannot be performed at runtime due to type erasure, such as ints
is List<Int> or list is T (type parameter). You can, however, check an instance against a star-projected type:
if (something is List<*>) {
something.forEach { println(it) } // The items are typed as `Any?`
}
227
Similarly, when you already have the type arguments of an instance checked statically (at compile time), you can make an
is -check or a cast that involves the non-generic part of the type. Note that angle brackets are omitted in this case:
The same syntax but with the type arguments omitted can be used for casts that do not take type arguments into
account: list as ArrayList.
Inline functions with reified type parameters have their actual type arguments inlined at each call site. This enables arg is T
checks for the type parameters, but if arg is an instance of a generic type itself, its type arguments are still erased.
//sampleStart
inline fun <reified A, reified B> Pair<*, *>.asPairOf(): Pair<A, B>? {
if (first !is A || second !is B) return null
return first as A to second as B
}
//sampleEnd
fun main() {
println("stringToSomething = " + stringToSomething)
println("stringToInt = " + stringToInt)
println("stringToList = " + stringToList)
println("stringToStringList = " + stringToStringList)
//println(stringToStringList?.second?.forEach() {it.length}) // This will throw
ClassCastException as list items are not String
}
Unchecked
casts
As established above, type erasure makes checking the actual type arguments of a generic type instance impossible at
runtime. Additionally, generic types in the code might not be connected to each other closely enough for the compiler to
ensure type safety.
Even so, sometimes we have high-level program logic that implies type safety instead. For example:
228
fun readDictionary(file: File): Map<String, *> = file.inputStream().use {
TODO("Read a mapping of strings to arbitrary elements.")
}
A warning appears for the cast in the last line. The compiler can't fully check it at runtime and provides no guarantee that
the values in the map are Int.
To avoid unchecked casts, you can redesign the program structure. In the example above, you could use the
DictionaryReader<T> and DictionaryWriter<T> interfaces with type-safe implementations for different types. You can
introduce reasonable abstractions to move unchecked casts from the call site to the implementation details. Proper use of
generic variance can also help.
For generic functions, using reified type parameters makes casts like arg as T checked, unless arg 's type has its own
type arguments that are erased.
An unchecked cast warning can be suppressed by annotating the statement or the declaration where it occurs with
@Suppress("UNCHECKED_CAST"):
On the JVM: array types (Array<Foo>) retain information about the erased type of their elements, and type casts
to an array type are partially checked: the nullability and actual type arguments of the element type are still
erased. For example, the cast foo as Array<List<String>?> will succeed if foo is an array holding any List<*>,
whether it is nullable or not.
Conditions
and
loops
If
expression
In Kotlin, if is an expression: it returns a value. Therefore, there is no ternary operator (condition ? then : else) because
ordinary if works fine in this role.
var max = a
if (a < b) max = b
229
// With else
var max: Int
if (a > b) {
max = a
} else {
max = b
}
// As expression
val max = if (a > b) a else b
Branches of if branches can be blocks. In this case, the last expression is the value of a block:
If you're using if as an expression, for example, for returning its value or assigning it to a variable, the else branch is
mandatory.
When
expression
when defines a conditional expression with multiple branches. It is similar to the switch statement in C-like languages. Its
simple form looks like this.
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}
when matches its argument against all branches sequentially until some branch condition is satisfied.
when can be used either as an expression or as a statement. If it is used as an expression, the value of the first matching
branch becomes the value of the overall expression. If it is used as a statement, the values of individual branches are
ignored. Just like with if, each branch can be a block, and its value is the value of the last expression in the block.
The else branch is evaluated if none of the other branch conditions are satisfied. If when is used as an expression, the
else branch is mandatory, unless the compiler can prove that all possible cases are covered with branch conditions, for
example, with enum class entries and sealed class subtypes).
230
}
To define a common behavior for multiple cases, combine their conditions in a single line with a comma:
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
You can use arbitrary expressions (not only constants) as branch conditions
when (x) {
s.toInt() -> print("s encodes x")
else -> print("s does not encode x")
}
You can also check a value for being in or !in a range or a collection:
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
Another option is checking that a value is or !is of a particular type. Note that, due to smart casts, you can access the
methods and properties of the type without any extra checks.
when can also be used as a replacement for an if- else if chain. If no argument is supplied, the branch conditions are
simply boolean expressions, and a branch is executed when its condition is true:
when {
x.isOdd() -> print("x is odd")
y.isEven() -> print("y is even")
else -> print("x+y is odd")
}
231
fun Request.getBody() =
when (val response = executeRequest()) {
is Success -> response.body
is HttpError -> throw HttpException(response.status)
}
The scope of variable introduced in when subject is restricted to the body of this when.
For
loops
The for loop iterates through anything that provides an iterator. This is equivalent to the foreach loop in languages like C#.
The syntax of for is the following:
As mentioned before, for iterates through anything that provides an iterator. This means that it:
fun main() {
//sampleStart
for (i in 1..3) {
println(i)
}
for (i in 6 downTo 0 step 2) {
println(i)
}
//sampleEnd
}
A for loop over a range or an array is compiled to an index-based loop that does not create an iterator object.
If you want to iterate through an array or a list with an index, you can do it this way:
fun main() {
val array = arrayOf("a", "b", "c")
232
//sampleStart
for (i in array.indices) {
println(array[i])
}
//sampleEnd
}
fun main() {
val array = arrayOf("a", "b", "c")
//sampleStart
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
//sampleEnd
}
While
loops
while and do-while loops execute their body continuously while their condition is satisfied. The difference between them is
the condition checking time:
while checks the condition and, if it's satisfied, executes the body and then returns to the condition check.
do-while executes the body and then checks the condition. If it's satisfied, the loop repeats. So, the body of do-while
executes at least once regardless of the condition.
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y is visible here!
Break
and
continue
in
loops
Kotlin supports traditional break and continue operators in loops. See Returns and jumps.
Returns
and
jumps
Kotlin has three structural jump expressions:
return by default returns from the nearest enclosing function or anonymous function
233
break terminates the nearest enclosing loop
Break
and
continue
labels
Any expression in Kotlin may be marked with a label. Labels have the form of an identifier followed by the @ sign, for
example: abc@, fooBar@. To label an expression, just add a label in front of it.
A break qualified with a label jumps to the execution point right after the loop marked with that label. A continue proceeds
to the next iteration of that loop.
Return
at
labels
With function literals, local functions and object expressions, functions can be nested in Kotlin. Qualified return s allow us
to return from an outer function. The most important use case is returning from a lambda expression. Recall that when we
write this:
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // non-local return directly to the caller of foo()
print(it)
}
println("this point is unreachable")
}
//sampleEnd
fun main() {
234
foo()
}
The return -expression returns from the nearest enclosing function - foo. Note that such non-local returns are supported
only for lambda expressions passed to inline functions. To return from a lambda expression, label it and qualify the return:
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // local return to the caller of the lambda - the forEach loop
print(it)
}
print(" done with explicit label")
}
//sampleEnd
fun main() {
foo()
}
Now, it returns only from the lambda expression. Oftentimes it is more convenient to use implicit labels: such a label has
the same name as the function to which the lambda is passed.
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // local return to the caller of the lambda - the forEach
loop
print(it)
}
print(" done with implicit label")
}
//sampleEnd
fun main() {
foo()
}
Alternatively, you can replace the lambda expression with an anonymous function. A return statement in an anonymous
function will return from the anonymous function itself.
//sampleStart
fun foo() {
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
if (value == 3) return // local return to the caller of the anonymous function - the
forEach loop
print(value)
})
print(" done with anonymous function")
}
//sampleEnd
fun main() {
foo()
235
}
Note that the use of local returns in previous three examples is similar to the use of continue in regular loops. There is no
direct equivalent for break, but it can be simulated by adding another nesting lambda and non-locally returning from it:
//sampleStart
fun foo() {
run loop@{
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@loop // non-local return from the lambda passed to run
print(it)
}
}
print(" done with nested loop")
}
//sampleEnd
fun main() {
foo()
}
When returning a value, the parser gives preference to the qualified return:
return@a 1
This means "return 1 at label @a" and not "return a labeled expression (@a 1)".
Exceptions
Exception
classes
All exception classes in Kotlin inherit the Throwable class. Every exception has a message, a stack trace, and an optional
cause.
fun main() {
//sampleStart
throw Exception("Hi There!")
//sampleEnd
}
try {
// some code
} catch (e: SomeException) {
236
// handler
} finally {
// optional finally block
}
There may be zero or more catch blocks, and the finally block may be omitted. However, at least one catch or finally block
is required.
Try
is
an
expression
try is an expression, which means it can have a return value:
The returned value of a try expression is either the last expression in the try block or the last expression in the catch block
(or blocks). The contents of the finally block don't affect the result of the expression.
Checked
exceptions
Kotlin does not have checked exceptions. There are many reasons for this, but we will provide a simple example that
illustrates why it is the case.
The following is an example interface from the JDK implemented by the StringBuilder class:
This signature says that every time I append a string to something (a StringBuilder, some kind of a log, a console, etc.), I
have to catch the IOExceptions. Why? Because the implementation might be performing IO operations (Writer also
implements Appendable). The result is code like this all over the place.:
try {
log.append(message)
} catch (IOException e) {
// Must be safe
}
And that’s not good. Just take a look at Effective Java, 3rd Edition, Item 77: Don't ignore exceptions.
Examination of small programs leads to the conclusion that requiring exception specifications could both
enhance developer productivity and enhance code quality, but experience with large software projects
suggests a different result – decreased productivity and little or no increase in code quality.
237
And here are some additional thoughts on the matter:
If you want to alert callers about possible exceptions when calling Kotlin code from Java, Swift, or Objective-C, you can
use the @Throws annotation. Read more about using this annotation for Java and for Swift and Objective-C.
The
Nothing
type
throw is an expression in Kotlin, so you can use it, for example, as part of an Elvis expression:
The throw expression has the type Nothing. This type has no values and is used to mark code locations that can never be
reached. In your own code, you can use Nothing to mark a function that never returns:
When you call this function, the compiler will know that the execution doesn't continue beyond the call:
You may also encounter this type when dealing with type inference. The nullable variant of this type, Nothing?, has
exactly one possible value, which is null. If you use null to initialize a value of an inferred type and there's no other
information that can be used to determine a more specific type, the compiler will infer the Nothing? type:
Java
interoperability
Please see the section on exceptions in the Java interoperability page for information about Java interoperability.
Packages
and
imports
A source file may start with a package declaration:
238
package org.example
// ...
All the contents, such as classes and functions, of the source file are included in this package. So, in the example above,
the full name of printMessage() is org.example.printMessage, and the full name of Message is org.example.Message.
If the package is not specified, the contents of such a file belong to the default package with no name.
Default
imports
A number of packages are imported into every Kotlin file by default:
kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparisons.*
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*
JVM:
java.lang.*
kotlin.jvm.*
JS:
kotlin.js.*
Imports
Apart from the default imports, each file may contain its own import directives.
239
import org.example.Message // Message is now accessible without qualification
or all the accessible contents of a scope: package, class, object, and so on:
If there is a name clash, you can disambiguate by using as keyword to locally rename the clashing entity:
The import keyword is not restricted to importing classes; you can also use it to import other declarations:
enum constants
Visibility
of
top-level
declarations
If a top-level declaration is marked private, it is private to the file it's declared in (see Visibility modifiers).
Classes
Classes in Kotlin are declared using the keyword class:
The class declaration consists of the class name, the class header (specifying its type parameters, the primary
constructor, and some other things), and the class body surrounded by curly braces. Both the header and the body are
optional; if the class has no body, the curly braces can be omitted.
class Empty
Constructors
A class in Kotlin can have a primary constructor and one or more secondary constructors. The primary constructor is a
part of the class header, and it goes after the class name and optional type parameters.
240
If the primary constructor does not have any annotations or visibility modifiers, the constructor keyword can be omitted:
The primary constructor cannot contain any code. Initialization code can be placed in initializer blocks prefixed with the
init keyword.
During the initialization of an instance, the initializer blocks are executed in the same order as they appear in the class
body, interleaved with the property initializers:
//sampleStart
class InitOrderDemo(name: String) {
val firstProperty = "First property: $name".also(::println)
init {
println("First initializer block that prints ${name}")
}
val secondProperty = "Second property: ${name.length}".also(::println)
init {
println("Second initializer block that prints ${name.length}")
}
}
//sampleEnd
fun main() {
InitOrderDemo("hello")
}
Primary constructor parameters can be used in the initializer blocks. They can also be used in property initializers
declared in the class body:
Kotlin has a concise syntax for declaring properties and initializing them from the primary constructor:
class Person(val firstName: String, val lastName: String, var age: Int)
Such declarations can also include default values of the class properties:
class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true)
You can use a trailing comma when you declare class properties:
class Person(
241
val firstName: String,
val lastName: String,
var age: Int, // trailing comma
) { /*...*/ }
Much like regular properties, properties declared in the primary constructor can be mutable (var) or read-only (val).
If the constructor has annotations or visibility modifiers, the constructor keyword is required and the modifiers go before
it:
Secondary
constructors
A class can also declare secondary constructors, which are prefixed with constructor:
class Pet {
constructor(owner: Person) {
owner.pets.add(this) // adds this pet to the list of its owner's pets
}
}
If the class has a primary constructor, each secondary constructor needs to delegate to the primary constructor, either
directly or indirectly through another secondary constructor(s). Delegation to another constructor of the same class is
done using the this keyword:
Code in initializer blocks effectively becomes part of the primary constructor. Delegation to the primary constructor
happens as the first statement of a secondary constructor, so the code in all initializer blocks and property initializers is
executed before the body of the secondary constructor.
Even if the class has no primary constructor, the delegation still happens implicitly, and the initializer blocks are still
executed:
//sampleStart
class Constructors {
init {
println("Init block")
}
242
constructor(i: Int) {
println("Constructor $i")
}
}
//sampleEnd
fun main() {
Constructors(1)
}
If a non-abstract class does not declare any constructors (primary or secondary), it will have a generated primary
constructor with no arguments. The visibility of the constructor will be public.
If you don't want your class to have a public constructor, declare an empty primary constructor with non-default visibility:
On the JVM, if all of the primary constructor parameters have default values, the compiler will generate an
additional parameterless constructor which will use the default values. This makes it easier to use Kotlin with
libraries such as Jackson or JPA that create class instances through parameterless constructors.
Creating
instances
of
classes
To create an instance of a class, call the constructor as if it were a regular function:
The process of creating instances of nested, inner, and anonymous inner classes is described in Nested classes.
Class
members
Classes can contain:
243
Functions
Properties
Object declarations
Inheritance
Classes can be derived from each other and form inheritance hierarchies. Learn more about inheritance in Kotlin.
Abstract
classes
A class may be declared abstract, along with some or all of its members. An abstract member does not have an
implementation in its class. You don't need to annotate abstract classes or functions with open.
Companion
objects
If you need to write a function that can be called without having a class instance but that needs access to the internals of
a class (such as a factory method), you can write it as a member of an object declaration inside that class.
Even more specifically, if you declare a companion object inside your class, you can access its members using only the
244
class name as a qualifier.
Inheritance
All classes in Kotlin have a common superclass, Any, which is the default superclass for a class with no supertypes
declared:
Any has three methods: equals(), hashCode(), and toString(). Thus, these methods are defined for all Kotlin classes.
By default, Kotlin classes are final – they can’t be inherited. To make a class inheritable, mark it with the open
keyword:
To declare an explicit supertype, place the type after a colon in the class header:
If the derived class has a primary constructor, the base class can (and must) be initialized in that primary constructor
according to its parameters.
If the derived class has no primary constructor, then each secondary constructor has to initialize the base type using the
super keyword or it has to delegate to another constructor which does. Note that in this case different secondary
constructors can call different constructors of the base type:
Overriding
methods
Kotlin requires explicit modifiers for overridable members and overrides:
245
class Circle() : Shape() {
override fun draw() { /*...*/ }
}
The override modifier is required for Circle.draw(). If it were missing, the compiler would complain. If there is no open
modifier on a function, like Shape.fill(), declaring a method with the same signature in a subclass is not allowed, either
with override or without it. The open modifier has no effect when added to members of a final class – a class without an
open modifier.
A member marked override is itself open, so it may be overridden in subclasses. If you want to prohibit re-overriding, use
final:
Overriding
properties
The overriding mechanism works on properties in the same way that it does on methods. Properties declared on a
superclass that are then redeclared on a derived class must be prefaced with override, and they must have a compatible
type. Each declared property can be overridden by a property with an initializer or by a property with a get method:
You can also override a val property with a var property, but not vice versa. This is allowed because a val property
essentially declares a get method, and overriding it as a var additionally declares a set method in the derived class.
Note that you can use the override keyword as part of the property declaration in a primary constructor:
interface Shape {
val vertexCount: Int
}
Derived
class
initialization
order
246
During the construction of a new instance of a derived class, the base class initialization is done as the first step
(preceded only by evaluation of the arguments for the base class constructor), which means that it happens before the
initialization logic of the derived class is run.
//sampleStart
open class Base(val name: String) {
class Derived(
name: String,
val lastName: String,
) : Base(name.replaceFirstChar { it.uppercase() }.also { println("Argument for the base class:
$it") }) {
fun main() {
println("Constructing the derived class(\"hello\", \"world\")")
Derived("hello", "world")
}
This means that when the base class constructor is executed, the properties declared or overridden in the derived class
have not yet been initialized. Using any of those properties in the base class initialization logic (either directly or indirectly
through another overridden open member implementation) may lead to incorrect behavior or a runtime failure. When
designing a base class, you should therefore avoid using open members in the constructors, property initializers, or init
blocks.
Calling
the
superclass
implementation
Code in a derived class can call its superclass functions and property accessor implementations using the super
keyword:
247
val fillColor: String get() = super.borderColor
}
Inside an inner class, accessing the superclass of the outer class is done using the super keyword qualified with the outer
class name: super@Outer:
//sampleStart
class FilledRectangle: Rectangle() {
override fun draw() {
val filler = Filler()
filler.drawAndFill()
}
inner class Filler {
fun fill() { println("Filling") }
fun drawAndFill() {
[email protected]() // Calls Rectangle's implementation of draw()
fill()
println("Drawn a filled rectangle with color ${[email protected]}")
// Uses Rectangle's implementation of borderColor's get()
}
}
}
//sampleEnd
fun main() {
val fr = FilledRectangle()
fr.draw()
}
Overriding
rules
In Kotlin, implementation inheritance is regulated by the following rule: if a class inherits multiple implementations of the
same member from its immediate superclasses, it must override this member and provide its own implementation
(perhaps, using one of the inherited ones).
To denote the supertype from which the inherited implementation is taken, use super qualified by the supertype name in
angle brackets, such as super<Base>:
interface Polygon {
fun draw() { /* ... */ } // interface members are 'open' by default
}
248
class Square() : Rectangle(), Polygon {
// The compiler requires draw() to be overridden:
override fun draw() {
super<Rectangle>.draw() // call to Rectangle.draw()
super<Polygon>.draw() // call to Polygon.draw()
}
}
It's fine to inherit from both Rectangle and Polygon, but both of them have their implementations of draw(), so you need to
override draw() in Square and provide a separate implementation for it to eliminate the ambiguity.
Properties
Declaring
properties
Properties in Kotlin classes can be declared either as mutable, using the var keyword, or as read-only, using the val
keyword.
class Address {
var name: String = "Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}
Getters
and
setters
The full syntax for declaring a property is as follows:
The initializer, getter, and setter are optional. The property type is optional if it can be inferred from the initializer or from
249
the initializer’s or the getter’s return type, as shown below:
The full syntax of a read-only property declaration differs from a mutable one in two ways: it starts with val instead of var
and does not allow a setter:
val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter
You can define custom accessors for a property. If you define a custom getter, it will be called every time you access the
property (this way you can implement a computed property). Here's an example of a custom getter:
//sampleStart
class Rectangle(val width: Int, val height: Int) {
val square: Int
get() = this.width * this.height
}
//sampleEnd
fun main() {
val rectangle = Rectangle(3, 4)
println("Width=${rectangle.width}, height=${rectangle.height}, square=${rectangle.square}")
}
You can omit the property type if it can be inferred from the getter:
If you define a custom setter, it will be called every time you assign a value to the property, except its initialization. A
custom setter looks like this:
By convention, the name of the setter parameter is value, but you can choose a different name if you prefer.
If you need to annotate an accessor or change its visibility, but you don't need to change the default implementation, you
can define the accessor without defining its body:
250
Backing
fields
In Kotlin, a field is only used as a part of a property to hold its value in memory. Fields cannot be declared directly.
However, when a property needs a backing field, Kotlin provides it automatically. This backing field can be referenced in
the accessors using the field identifier:
The field identifier can only be used in the accessors of the property.
A backing field will be generated for a property if it uses the default implementation of at least one of the accessors, or if a
custom accessor references it through the field identifier.
Backing
properties
If you want to do something that does not fit into this implicit backing field scheme, you can always fall back to having a
backing property:
On the JVM: Access to private properties with default getters and setters is optimized to avoid function call
overhead.
Compile-time
constants
If the value of a read-only property is known at compile time, mark it as a compile time constant using the const modifier.
Such a property needs to fulfil the following requirements:
251
It must be initialized with a value of type String or a primitive type
Late-initialized
properties
and
variables
Normally, properties declared as having a non-null type must be initialized in the constructor. However, it is often the case
that doing so is not convenient. For example, properties can be initialized through dependency injection, or in the setup
method of a unit test. In these cases, you cannot supply a non-null initializer in the constructor, but you still want to avoid
null checks when referencing the property inside the body of a class.
To handle such cases, you can mark the property with the lateinit modifier:
This modifier can be used on var properties declared inside the body of a class (not in the primary constructor, and only
when the property does not have a custom getter or setter), as well as for top-level properties and local variables. The
type of the property or variable must be non-null, and it must not be a primitive type.
Accessing a lateinit property before it has been initialized throws a special exception that clearly identifies the property
being accessed and the fact that it hasn't been initialized.
Checking
whether
a
lateinit
var
is
initialized
To check whether a lateinit var has already been initialized, use .isInitialized on the reference to that property:
if (foo::bar.isInitialized) {
println(foo.bar)
}
This check is only available for properties that are lexically accessible when declared in the same type, in one of the outer
types, or at top level in the same file.
252
Overriding
properties
See Overriding properties
Delegated
properties
The most common kind of property simply reads from (and maybe writes to) a backing field, but custom getters and
setters allow you to use properties so one can implement any sort of behavior of a property. Somewhere in between the
simplicity of the first kind and variety of the second, there are common patterns for what properties can do. A few
examples: lazy values, reading from a map by a given key, accessing a database, notifying a listener on access.
Interfaces
Interfaces in Kotlin can contain declarations of abstract methods, as well as method implementations. What makes them
different from abstract classes is that interfaces cannot store a state. They can have properties, but these need to be
abstract or provide accessor implementations.
interface MyInterface {
fun bar()
fun foo() {
// optional body
}
}
Implementing
interfaces
A class or object can implement one or more interfaces:
Properties
in
interfaces
You can declare properties in interfaces. A property declared in an interface can either be abstract or provide
implementations for accessors. Properties declared in interfaces can't have backing fields, and therefore accessors
253
declared in interfaces can't reference them:
interface MyInterface {
val prop: Int // abstract
fun foo() {
print(prop)
}
}
Interfaces
Inheritance
An interface can derive from other interfaces, meaning it can both provide implementations for their members and declare
new functions and properties. Quite naturally, classes implementing such an interface are only required to define the
missing implementations:
interface Named {
val name: String
}
Resolving
overriding
conflicts
When you declare many types in your supertype list, you may inherit more than one implementation of the same method:
interface A {
fun foo() { print("A") }
fun bar()
}
interface B {
254
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
Interfaces A and B both declare functions foo() and bar(). Both of them implement foo(), but only B implements bar() (bar()
is not marked as abstract in A, because this is the default for interfaces if the function has no body). Now, if you derive a
concrete class C from A, you have to override bar() and provide an implementation.
However, if you derive D from A and B, you need to implement all the methods that you have inherited from multiple
interfaces, and you need to specify how exactly D should implement them. This rule applies both to methods for which
you've inherited a single implementation (bar()) and to those for which you've inherited multiple implementations (foo()).
Functional
(SAM)
interfaces
An interface with only one abstract method is called a functional interface, or a Single Abstract Method (SAM) interface.
The functional interface can have several non-abstract members but only one abstract member.
SAM
conversions
For functional interfaces, you can use SAM conversions that help make your code more concise and readable by using
lambda expressions.
Instead of creating a class that implements a functional interface manually, you can use a lambda expression. With a
SAM conversion, Kotlin can convert any lambda expression whose signature matches the signature of the interface's
single method into the code, which dynamically instantiates the interface implementation.
255
fun interface IntPredicate {
fun accept(i: Int): Boolean
}
If you don't use a SAM conversion, you will need to write code like this:
By leveraging Kotlin's SAM conversion, you can write the following equivalent code instead:
fun main() {
println("Is 7 even? - ${isEven.accept(7)}")
}
Functional
interfaces
vs.
type
aliases
Functional interfaces and type aliases serve different purposes. Type aliases are just names for existing types – they
don't create a new type, while functional interfaces do. You can provide extensions that are specific to a particular
functional interface to be inapplicable for plain functions or their type aliases.
Type aliases can have only one member, while functional interfaces can have multiple non-abstract members and one
abstract member. Functional interfaces can also implement and extend other interfaces.
Functional interfaces are more flexible and provide more capabilities than type aliases, but they can be more costly both
syntactically and at runtime because they can require conversions to a specific interface. When you choose which one to
use in your code, consider your needs:
If your API needs to accept a function (any function) with some specific parameter and return types – use a simple
functional type or define a type alias to give a shorter name to the corresponding functional type.
256
If your API accepts a more complex entity than a function – for example, it has non-trivial contracts and/or
operations on it that can't be expressed in a functional type's signature – declare a separate functional interface for
it.
Visibility
modifiers
Classes, objects, interfaces, constructors, and functions, as well as properties and their setters, can have visibility
modifiers. Getters always have the same visibility as their properties.
There are four visibility modifiers in Kotlin: private, protected, internal, and public. The default visibility is public.
On this page, you'll learn how the modifiers apply to different types of declaring scopes.
Packages
Functions, properties, classes, objects, and interfaces can be declared at the "top-level" directly inside a package:
If you don’t use a visibility modifier, public is used by default, which means that your declarations will be visible
everywhere.
If you mark a declaration as private, it will only be visible inside the file that contains the declaration.
To use a visible top-level declaration from another package, you should import it.
Examples:
257
Class
members
For members declared inside a class:
private means that the member is visible inside this class only (including all its members).
protected means that the member has the same visibility as one marked as private, but that it is also visible in
subclasses.
internal means that any client inside this module who sees the declaring class sees its internal members.
public means that any client who sees the declaring class sees its public members.
In Kotlin, an outer class does not see private members of its inner classes.
If you override a protected or an internal member and do not specify the visibility explicitly, the overriding member will
also have the same visibility as the original.
Examples:
Constructors
Use the following syntax to specify the visibility of the primary constructor of a class:
258
You need to add an explicit constructor keyword.
Here the constructor is private. By default, all constructors are public, which effectively amounts to them being visible
everywhere the class is visible (this means that a constructor of an internal class is only visible within the same module).
Local
declarations
Local variables, functions, and classes can't have visibility modifiers.
Modules
The internal visibility modifier means that the member is visible within the same module. More specifically, a module is a
set of Kotlin files compiled together, for example:
A Maven project.
A Gradle source set (with the exception that the test source set can access the internal declarations of main).
A set of files compiled with one invocation of the <kotlinc> Ant task.
Extensions
Kotlin provides the ability to extend a class with new functionality without having to inherit from the class or use design
patterns such as Decorator. This is done via special declarations called extensions.
For example, you can write new functions for a class from a third-party library that you can't modify. Such functions can
be called in the usual way, as if they were methods of the original class. This mechanism is called an extension function.
There are also extension properties that let you define new properties for existing classes.
Extension
functions
To declare an extension function, prefix its name with a receiver type, which refers to the type being extended. The
following adds a swap function to MutableList<Int>:
259
}
The this keyword inside an extension function corresponds to the receiver object (the one that is passed before the dot).
Now, you can call such a function on any MutableList<Int>:
This function makes sense for any MutableList<T>, and you can make it generic:
You need to declare the generic type parameter before the function name to make it available in the receiver type
expression. For more information about generics, see generic functions.
Extensions
are
resolved
statically
Extensions do not actually modify the classes they extend. By defining an extension, you are not inserting new members
into a class, only making new functions callable with the dot-notation on variables of this type.
Extension functions are dispatched statically, which means they are not virtual by receiver type. An extension function
being called is determined by the type of the expression on which the function is invoked, not by the type of the result
from evaluating that expression at runtime. For example:
fun main() {
//sampleStart
open class Shape
class Rectangle: Shape()
fun Shape.getName() = "Shape"
fun Rectangle.getName() = "Rectangle"
fun printClassName(s: Shape) {
println(s.getName())
}
printClassName(Rectangle())
//sampleEnd
}
This example prints Shape, because the extension function called depends only on the declared type of the parameter s,
which is the Shape class.
If a class has a member function, and an extension function is defined which has the same receiver type, the same name,
and is applicable to given arguments, the member always wins. For example:
260
fun main() {
//sampleStart
class Example {
fun printFunctionType() { println("Class method") }
}
fun Example.printFunctionType() { println("Extension function") }
Example().printFunctionType()
//sampleEnd
}
However, it's perfectly OK for extension functions to overload member functions that have the same name but a different
signature:
fun main() {
//sampleStart
class Example {
fun printFunctionType() { println("Class method") }
}
fun Example.printFunctionType(i: Int) { println("Extension function #$i") }
Example().printFunctionType(1)
//sampleEnd
}
Nullable
receiver
Note that extensions can be defined with a nullable receiver type. These extensions can be called on an object variable
even if its value is null, and they can check for this == null inside the body.
This way, you can call toString() in Kotlin without checking for null, as the check happens inside the extension function:
Extension
properties
Kotlin supports extension properties much like it supports functions:
261
Since extensions do not actually insert members into classes, there's no efficient way for an extension property
to have a backing field. This is why initializers are not allowed for extension properties. Their behavior can only
be defined by explicitly providing getters/setters.
Example:
val House.number = 1 // error: initializers are not allowed for extension properties
Companion
object
extensions
If a class has a companion object defined, you can also define extension functions and properties for the companion
object. Just like regular members of the companion object, they can be called using only the class name as the qualifier:
class MyClass {
companion object { } // will be called "Companion"
}
fun main() {
MyClass.printCompanion()
}
Scope
of
extensions
In most cases, you define extensions on the top level, directly under packages:
package org.example.declarations
To use an extension outside its declaring package, import it at the call site:
package org.example.usage
import org.example.declarations.getLongestString
fun main() {
val list = listOf("red", "green", "blue")
list.getLongestString()
}
262
See Imports for more information.
Declaring
extensions
as
members
You can declare extensions for one class inside another class. Inside such an extension, there are multiple implicit
receivers- objects whose members can be accessed without a qualifier. An instance of a class in which the extension is
declared is called a dispatch receiver, and an instance of the receiver type of the extension method is called an extension
receiver.
fun Host.printConnectionString() {
printHostname() // calls Host.printHostname()
print(":")
printPort() // calls Connection.printPort()
}
fun connect() {
/*...*/
host.printConnectionString() // calls the extension function
}
}
fun main() {
Connection(Host("kotl.in"), 443).connect()
//Host("kotl.in").printConnectionString() // error, the extension function is unavailable
outside Connection
}
In the event of a name conflict between the members of a dispatch receiver and an extension receiver, the extension
receiver takes precedence. To refer to the member of the dispatch receiver, you can use the qualified this syntax.
class Connection {
fun Host.getConnectionString() {
toString() // calls Host.toString()
[email protected]() // calls Connection.toString()
}
}
Extensions declared as members can be declared as open and overridden in subclasses. This means that the dispatch of
such functions is virtual with regard to the dispatch receiver type, but static with regard to the extension receiver type.
263
open fun Base.printFunctionInfo() {
println("Base extension function in BaseCaller")
}
fun main() {
BaseCaller().call(Base()) // "Base extension function in BaseCaller"
DerivedCaller().call(Base()) // "Base extension function in DerivedCaller" - dispatch
receiver is resolved virtually
DerivedCaller().call(Derived()) // "Base extension function in DerivedCaller" - extension
receiver is resolved statically
}
Note
on
visibility
Extensions utilize the same visibility modifiers as regular functions declared in the same scope would. For example:
An extension declared at the top level of a file has access to the other private top-level declarations in the same file.
If an extension is declared outside its receiver type, it cannot access the receiver's private or protected members.
Data
classes
It is not unusual to create classes whose main purpose is to hold data. In such classes, some standard functionality and
some utility functions are often mechanically derivable from the data. In Kotlin, these are called data classes and are
marked with data:
The compiler automatically derives the following members from all properties declared in the primary constructor:
264
toString() of the form "User(name=John, age=42)"
To ensure consistency and meaningful behavior of the generated code, data classes have to fulfill the following
requirements:
Additionally, the generation of data class members follows these rules with regard to the members’ inheritance:
If there are explicit implementations of equals(), hashCode(), or toString() in the data class body or final
implementations in a superclass, then these functions are not generated, and the existing implementations are used.
If a supertype has componentN() functions that are open and return compatible types, the corresponding functions are
generated for the data class and override those of the supertype. If the functions of the supertype cannot be
overridden due to incompatible signatures or due to their being final, an error is reported.
Providing explicit implementations for the componentN() and copy() functions is not allowed.
Data classes may extend other classes (see Sealed classes for examples).
On the JVM, if the generated class needs to have a parameterless constructor, default values for the properties
have to be specified (see Constructors).
Properties
declared
in
the
class
body
The compiler only uses the properties defined inside the primary constructor for the automatically generated functions. To
exclude a property from the generated implementations, declare it inside the class body:
Only the property name will be used inside the toString(), equals(), hashCode(), and copy() implementations, and there will
only be one component function component1(). While two Person objects can have different ages, they will be treated as
equal.
265
data class Person(val name: String) {
var age: Int = 0
}
fun main() {
//sampleStart
val person1 = Person("John")
val person2 = Person("John")
person1.age = 10
person2.age = 20
//sampleEnd
println("person1 == person2: ${person1 == person2}")
println("person1 with age ${person1.age}: ${person1}")
println("person2 with age ${person2.age}: ${person2}")
}
Copying
Use the copy() function to copy an object, allowing you to alter some of its properties while keeping the rest unchanged.
The implementation of this function for the User class above would be as follows:
Data
classes
and
destructuring
declarations
Component functions generated for data classes make it possible to use them in destructuring declarations:
Standard
data
classes
The standard library provides the Pair and Triple classes. In most cases, though, named data classes are a better design
choice because they make the code more readable by providing meaningful names for the properties.
Sealed
classes
Sealed classes and interfaces represent restricted class hierarchies that provide more control over inheritance. All direct
266
subclasses of a sealed class are known at compile time. No other subclasses may appear after a module with the sealed
class is compiled. For example, third-party clients can't extend your sealed class in their code. Thus, each instance of a
sealed class has a type from a limited set that is known when this class is compiled.
The same works for sealed interfaces and their implementations: once a module with a sealed interface is compiled, no
new implementations can appear.
In some sense, sealed classes are similar to enum classes: the set of values for an enum type is also restricted, but each
enum constant exists only as a single instance, whereas a subclass of a sealed class can have multiple instances, each
with its own state.
As an example, consider a library's API. It's likely to contain error classes to let the library users handle errors that it can
throw. If the hierarchy of such error classes includes interfaces or abstract classes visible in the public API, then nothing
prevents implementing or extending them in the client code. However, the library doesn't know about errors declared
outside it, so it can't treat them consistently with its own classes. With a sealed hierarchy of error classes, library authors
can be sure that they know all possible error types and no other ones can appear later.
To declare a sealed class or interface, put the sealed modifier before its name:
A sealed class is abstract by itself, it cannot be instantiated directly and can have abstract members.
Constructors of sealed classes can have one of two visibilities: protected (by default) or private:
Location
of
direct
subclasses
Direct subclasses of sealed classes and interfaces must be declared in the same package. They may be top-level or
nested inside any number of other named classes, named interfaces, or named objects. Subclasses can have any
visibility as long as they are compatible with normal inheritance rules in Kotlin.
Subclasses of sealed classes must have a proper qualified name. They can't be local nor anonymous objects.
267
enum classes can't extend a sealed class (as well as any other class), but they can implement sealed
interfaces.
These restrictions don't apply to indirect subclasses. If a direct subclass of a sealed class is not marked as sealed, it can
be extended in any ways that its modifiers allow:
sealed interface Error // has implementations only in same package and module
sealed class IOError(): Error // extended only in same package and module
open class CustomError(): Error // can be extended wherever it's visible
Inheritance
in
multiplatform
projects
There is one more inheritance restriction in multiplatform projects: direct subclasses of sealed classes must reside in the
same source set. It applies to sealed classes without the expect and actual modifiers.
If a sealed class is declared as expect in a common source set and have actual implementations in platform source sets,
both expect and actual versions can have subclasses in their source sets. Moreover, if you use a hierarchical structure,
you can create subclasses in any source set between the expect and actual declarations.
Sealed
classes
and
when
expression
The key benefit of using sealed classes comes into play when you use them in a when expression. If it's possible to verify
that the statement covers all cases, you don't need to add an else clause to the statement. However, this works only if
you use when as an expression (using the result) and not as a statement:
when expressions on expect sealed classes in the common code of multiplatform projects still require an else
branch. This happens because subclasses of actual platform implementations aren't known in the common
code.
Generics:
in,
out,
where
268
Classes in Kotlin can have type parameters, just like in Java:
class Box<T>(t: T) {
var value = t
}
But if the parameters can be inferred, for example, from the constructor arguments, you can omit the type arguments:
val box = Box(1) // 1 has type Int, so the compiler figures out that it is Box<Int>
Variance
One of the trickiest aspects of Java's type system is the wildcard types (see Java Generics FAQ). Kotlin doesn't have
these. Instead, Kotlin has declaration-site variance and type projections.
Let's think about why Java needs these mysterious wildcards. The problem is explained well in Effective Java, 3rd
Edition, Item 31: Use bounded wildcards to increase API flexibility. First, generic types in Java are invariant, meaning that
List<String> is not a subtype of List<Object>. If List were not invariant, it would have been no better than Java's arrays,
as the following code would have compiled but caused an exception at runtime:
// Java
List<String> strs = new ArrayList<String>();
List<Object> objs = strs; // !!! A compile-time error here saves us from a runtime exception
later.
objs.add(1); // Put an Integer into a list of Strings
String s = strs.get(0); // !!! ClassCastException: Cannot cast Integer to String
Java prohibits such things in order to guarantee run-time safety. But this has implications. For example, consider the
addAll() method from the Collection interface. What's the signature of this method? Intuitively, you'd write it this way:
// Java
interface Collection<E> ... {
void addAll(Collection<E> items);
}
But then, you would not be able to do the following (which is perfectly safe):
// Java
void copyAll(Collection<Object> to, Collection<String> from) {
to.addAll(from);
// !!! Would not compile with the naive declaration of addAll:
// Collection<String> is not a subtype of Collection<Object>
269
}
(In Java, you probably learned this the hard way, see Effective Java, 3rd Edition, Item 28: Prefer lists to arrays)
// Java
interface Collection<E> ... {
void addAll(Collection<? extends E> items);
}
The wildcard type argument ? extends E indicates that this method accepts a collection of objects of E or a subtype of E,
not just E itself. This means that you can safely read E 's from items (elements of this collection are instances of a
subclass of E), but cannot write to it as you don't know what objects comply with that unknown subtype of E. In return for
this limitation, you get the desired behavior: Collection<String> is a subtype of Collection<? extends Object>. In other
words, the wildcard with an extends -bound (upper bound) makes the type covariant.
The key to understanding why this works is rather simple: if you can only take items from a collection, then using a
collection of String s and reading Object s from it is fine. Conversely, if you can only put items into the collection, it's okay
to take a collection of Object s and put String s into it: in Java there is List<? super String>, a supertype of List<Object>.
The latter is called contravariance, and you can only call methods that take String as an argument on List<? super String>
(for example, you can call add(String) or set(int, String)). If you call something that returns T in List<T>, you don't get a
String, but rather an Object.
Joshua Bloch gives the name Producers to objects you only read from and Consumers to those you only write to. He
recommends:
"For maximum flexibility, use wildcard types on input parameters that represent producers or consumers", and
proposes the following mnemonic:
If you use a producer-object, say, List<? extends Foo>, you are not allowed to call add() or set() on this object,
but this does not mean that it is immutable: for example, nothing prevents you from calling clear() to remove all
the items from the list, since clear() does not take any parameters at all.
The only thing guaranteed by wildcards (or other types of variance) is type safety. Immutability is a completely
different story.
Declaration-site
variance
Let's suppose that there is a generic interface Source<T> that does not have any methods that take T as a parameter,
only methods that return T:
270
// Java
interface Source<T> {
T nextT();
}
Then, it would be perfectly safe to store a reference to an instance of Source<String> in a variable of type
Source<Object>- there are no consumer-methods to call. But Java does not know this, and still prohibits it:
// Java
void demo(Source<String> strs) {
Source<Object> objects = strs; // !!! Not allowed in Java
// ...
}
To fix this, you should declare objects of type Source<? extends Object>. Doing so is meaningless, because you can call
all the same methods on such a variable as before, so there's no value added by the more complex type. But the
compiler does not know that.
In Kotlin, there is a way to explain this sort of thing to the compiler. This is called declaration-site variance: you can
annotate the type parameter T of Source to make sure that it is only returned (produced) from members of Source<T>,
and never consumed. To do this, use the out modifier:
The general rule is this: when a type parameter T of a class C is declared out, it may occur only in the out -position in the
members of C, but in return C<Base> can safely be a supertype of C<Derived>.
In other words, you can say that the class C is covariant in the parameter T, or that T is a covariant type parameter. You
can think of C as being a producer of T 's, and NOT a consumer of T 's.
The out modifier is called a variance annotation, and since it is provided at the type parameter declaration site, it provides
declaration-site variance. This is in contrast with Java's use-site variance where wildcards in the type usages make the
types covariant.
In addition to out, Kotlin provides a complementary variance annotation: in. It makes a type parameter contravariant,
meaning it can only be consumed and never produced. A good example of a contravariant type is Comparable:
271
// Thus, you can assign x to a variable of type Comparable<Double>
val y: Comparable<Double> = x // OK!
}
The words in and out seem to be self-explanatory (as they’ve already been used successfully in C# for quite some
time), and so the mnemonic mentioned above is not really needed. It can in fact be rephrased at a higher level of
abstraction:
Type
projections
Use-site
variance:
type
projections
It is very easy to declare a type parameter T as out and avoid trouble with subtyping on the use site, but some classes
can't actually be restricted to only return T 's! A good example of this is Array:
This class can be neither co- nor contravariant in T. And this imposes certain inflexibilities. Consider the following
function:
This function is supposed to copy items from one array to another. Let's try to apply it in practice:
Here you run into the same familiar problem: Array<T> is invariant in T, and so neither Array<Int> nor Array<Any> is a
subtype of the other. Why not? Again, this is because copy could have an unexpected behavior, for example, it may
attempt to write a String to from, and if you actually pass an array of Int there, a ClassCastException will be thrown later.
To prohibit the copy function from writing to from, you can do the following:
272
This is type projection, which means that from is not a simple array, but is rather a restricted (projected) one. You can only
call methods that return the type parameter T, which in this case means that you can only call get(). This is our approach
to use-site variance, and it corresponds to Java's Array<? extends Object> while being slightly simpler.
Array<in String> corresponds to Java's Array<? super String>. This means that you can pass an array of CharSequence
or an array of Object to the fill() function.
Star-projections
Sometimes you want to say that you know nothing about the type argument, but you still want to use it in a safe way. The
safe way here is to define such a projection of the generic type, that every concrete instantiation of that generic type will
be a subtype of that projection.
For Foo<out T : TUpper>, where T is a covariant type parameter with the upper bound TUpper, Foo<*> is equivalent to
Foo<out TUpper>. This means that when the T is unknown you can safely read values of TUpper from Foo<*>.
For Foo<in T>, where T is a contravariant type parameter, Foo<*> is equivalent to Foo<in Nothing>. This means there
is nothing you can write to Foo<*> in a safe way when T is unknown.
For Foo<T : TUpper>, where T is an invariant type parameter with the upper bound TUpper, Foo<*> is equivalent to
Foo<out TUpper> for reading values and to Foo<in Nothing> for writing values.
If a generic type has several type parameters, each of them can be projected independently. For example, if the type is
declared as interface Function<in T, out U> you could use the following star-projections:
Star-projections are very much like Java's raw types, but safe.
Generic
functions
Classes aren’t the only declarations that can have type parameters. Functions can, too. Type parameters are placed
before the name of the function:
273
}
To call a generic function, specify the type arguments at the call site after the name of the function:
val l = singletonList<Int>(1)
Type arguments can be omitted if they can be inferred from the context, so the following example works as well:
val l = singletonList(1)
Generic
constraints
The set of all possible types that can be substituted for a given type parameter may be restricted by generic constraints.
Upper
bounds
The most common type of constraint is an upper bound, which corresponds to Java's extends keyword:
The type specified after a colon is the upper bound, indicating that only a subtype of Comparable<T> can be substituted
for T. For example:
The default upper bound (if there was none specified) is Any?. Only one upper bound can be specified inside the angle
brackets. If the same type parameter needs more than one upper bound, you need a separate where -clause:
The passed type must satisfy all conditions of the where clause simultaneously. In the above example, the T type must
implement both CharSequence and Comparable.
274
Type
erasure
The type safety checks that Kotlin performs for generic declaration usages are done at compile time. At runtime, the
instances of generic types do not hold any information about their actual type arguments. The type information is said to
be erased. For example, the instances of Foo<Bar> and Foo<Baz?> are erased to just Foo<*>.
Therefore, there is no general way to check whether an instance of a generic type was created with certain type
arguments at runtime, and the compiler prohibits such is -checks.
Type casts to generic types with concrete type arguments, for example, foo as List<String>, cannot be checked at
runtime. These unchecked casts can be used when type safety is implied by high-level program logic but cannot be
inferred directly by the compiler. The compiler issues a warning on unchecked casts, and at runtime, only the non-generic
part is checked (equivalent to foo as List<*>).
The type arguments of generic function calls are also only checked at compile time. Inside the function bodies, the type
parameters cannot be used for type checks, and type casts to type parameters (foo as T) are unchecked. However, reified
type parameters of inline functions are substituted by the actual type arguments in the inlined function body at the call
sites and so can be used for type checks and casts, with the same restrictions for instances of generic types as described
above.
Nested
and
inner
classes
Classes can be nested in other classes:
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
You can also use interfaces with nesting. All combinations of classes and interfaces are possible: You can nest interfaces
in classes, classes in interfaces, and interfaces in interfaces.
interface OuterInterface {
class InnerClass
interface InnerInterface
}
class OuterClass {
class InnerClass
interface InnerInterface
}
275
Inner
classes
A nested class marked as inner can access the members of its outer class. Inner classes carry a reference to an object of
an outer class:
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
See Qualified this expressions to learn about disambiguation of this in inner classes.
Anonymous
inner
classes
Anonymous inner class instances are created using an object expression:
window.addMouseListener(object : MouseAdapter() {
On the JVM, if the object is an instance of a functional Java interface (that means a Java interface with a single
abstract method), you can create it using a lambda expression prefixed with the type of the interface:
Enum
classes
The most basic use case for enum classes is the implementation of type-safe enums:
Since each enum is an instance of the enum class, it can be initialized as:
276
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
Anonymous
classes
Enum constants can declare their own anonymous classes with their corresponding methods, as well as with overriding
base methods.
TALKING {
override fun signal() = WAITING
};
If the enum class defines any members, separate the constant definitions from the member definitions with a semicolon.
Implementing
interfaces
in
enum
classes
An enum class can implement an interface (but it cannot derive from a class), providing either a common implementation
of interface members for all of the entries, or separate implementations for each entry within its anonymous class. This is
done by adding the interfaces you want to implement to the enum class declaration as follows:
import java.util.function.BinaryOperator
import java.util.function.IntBinaryOperator
//sampleStart
enum class IntArithmetics : BinaryOperator<Int>, IntBinaryOperator {
PLUS {
override fun apply(t: Int, u: Int): Int = t + u
},
TIMES {
override fun apply(t: Int, u: Int): Int = t * u
};
override fun applyAsInt(t: Int, u: Int) = apply(t, u)
}
//sampleEnd
fun main() {
val a = 13
277
val b = 31
for (f in IntArithmetics.values()) {
println("$f($a, $b) = ${f.apply(a, b)}")
}
}
Working
with
enum
constants
Enum classes in Kotlin have synthetic methods for listing the defined enum constants and getting an enum constant by
its name. The signatures of these methods are as follows (assuming the name of the enum class is EnumClass):
The valueOf() method throws an IllegalArgumentException if the specified name does not match any of the enum
constants defined in the class.
You can access the constants in an enum class in a generic way using the enumValues<T>() and enumValueOf<T>()
functions:
Every enum constant has properties for obtaining its name and position in the enum class declaration:
The enum constants also implement the Comparable interface, with the natural order being the order in which they are
defined in the enum class.
Inline
classes
Sometimes it is necessary for business logic to create a wrapper around some type. However, it introduces runtime
overhead due to additional heap allocations. Moreover, if the wrapped type is primitive, the performance hit is terrible,
because primitive types are usually heavily optimized by the runtime, while their wrappers don't get any special treatment.
To solve such issues, Kotlin introduces a special kind of class called an inline class. Inline classes are a subset of value-
based classes. They don't have an identity and can only hold values.
278
To declare an inline class, use the value modifier before the name of the class:
To declare an inline class for the JVM backend, use the value modifier along with the @JvmInline annotation before the
class declaration:
An inline class must have a single property initialized in the primary constructor. At runtime, instances of the inline class
will be represented using this single property (see details about runtime representation below):
This is the main feature of inline classes, which inspired the name inline: data of the class is inlined into its usages (similar
to how content of inline functions is inlined to call sites).
Members
Inline classes support some functionality of regular classes. In particular, they are allowed to declare properties and
functions, and have the init block:
@JvmInline
value class Name(val s: String) {
init {
require(s.length > 0) { }
}
fun greet() {
println("Hello, $s")
}
}
fun main() {
val name = Name("Kotlin")
name.greet() // method `greet` is called as a static method
println(name.length) // property getter is called as a static method
}
279
Inline class properties cannot have backing fields. They can only have simple computable properties (no lateinit
/delegated properties).
Inheritance
Inline classes are allowed to inherit from interfaces:
interface Printable {
fun prettyPrint(): String
}
@JvmInline
value class Name(val s: String) : Printable {
override fun prettyPrint(): String = "Let's $s!"
}
fun main() {
val name = Name("Kotlin")
println(name.prettyPrint()) // Still called as a static method
}
It is forbidden for inline classes to participate in a class hierarchy. This means that inline classes cannot extend other
classes and must be final.
Representation
In generated code, the Kotlin compiler keeps a wrapper for each inline class. Inline class instances can be represented at
runtime either as wrappers or as the underlying type. This is similar to how Int can be represented either as a primitive int
or as the wrapper Integer.
The Kotlin compiler will prefer using underlying types instead of wrappers to produce the most performant and optimized
code. However, sometimes it is necessary to keep wrappers around. As a rule of thumb, inline classes are boxed
whenever they are used as another type.
interface I
@JvmInline
value class Foo(val i: Int) : I
fun main() {
val f = Foo(42)
280
asInline(f) // unboxed: used as Foo itself
asGeneric(f) // boxed: used as generic type T
asInterface(f) // boxed: used as type I
asNullable(f) // boxed: used as Foo?, which is different from Foo
// below, 'f' first is boxed (while being passed to 'id') and then unboxed (when returned
from 'id')
// In the end, 'c' contains unboxed representation (just '42'), as 'f'
val c = id(f)
}
Because inline classes may be represented both as the underlying value and as a wrapper, referential equality is pointless
for them and is therefore prohibited.
Mangling
Since inline classes are compiled to their underlying type, it may lead to various obscure errors, for example unexpected
platform signature clashes:
@JvmInline
value class UInt(val x: Int)
To mitigate such issues, functions using inline classes are mangled by adding some stable hashcode to the function
name. Therefore, fun compute(x: UInt) will be represented as public final void compute-<hashcode>(int x), which solves
the clash problem.
The mangling scheme has been changed in Kotlin 1.4.30. Use the -Xuse-14-inline-classes-mangling-scheme
compiler flag to force the compiler to use the old 1.4.0 mangling scheme and preserve binary compatibility.
Calling
from
Java
code
You can call functions that accept inline classes from Java code. To do so, you should manually disable mangling: add
the @JvmName annotation before the function declaration:
@JvmInline
value class UInt(val x: Int)
@JvmName("computeUInt")
fun compute(x: UInt) { }
281
Inline
classes
vs
type
aliases
At first sight, inline classes seem very similar to type aliases. Indeed, both seem to introduce a new type and both will be
represented as the underlying type at runtime.
However, the crucial difference is that type aliases are assignment-compatible with their underlying type (and with other
type aliases with the same underlying type), while inline classes are not.
In other words, inline classes introduce a truly new type, contrary to type aliases which only introduce an alternative name
(alias) for an existing type:
@JvmInline
value class NameInlineClass(val s: String)
fun main() {
val nameAlias: NameTypeAlias = ""
val nameInlineClass: NameInlineClass = NameInlineClass("")
val string: String = ""
Object
expressions
and
declarations
Sometimes you need to create an object that is a slight modification of some class, without explicitly declaring a new
subclass for it. Kotlin can handle this with object expressions and object declarations.
Object
expressions
Object expressions create objects of anonymous classes, that is, classes that aren't explicitly declared with the class
declaration. Such classes are useful for one-time use. You can define them from scratch, inherit from existing classes, or
implement interfaces. Instances of anonymous classes are also called anonymous objects because they are defined by
an expression, not a name.
Creating
anonymous
objects
from
scratch
Object expressions start with the object keyword.
282
If you just need an object that doesn’t have any nontrivial supertypes, write its members in curly braces after object:
fun main() {
//sampleStart
val helloWorld = object {
val hello = "Hello"
val world = "World"
// object expressions extend Any, so `override` is required on `toString()`
override fun toString() = "$hello $world"
}
//sampleEnd
print(helloWorld)
}
Inheriting
anonymous
objects
from
supertypes
To create an object of an anonymous class that inherits from some type (or types), specify this type after object and a
colon (:). Then implement or override the members of this class as if you were inheriting from it:
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { /*...*/ }
If a supertype has a constructor, pass appropriate constructor parameters to it. Multiple supertypes can be specified as a
comma-delimited list after the colon:
interface B { /*...*/ }
Using
anonymous
objects
as
return
and
value
types
When an anonymous object is used as a type of a local or private but not inline declaration (function or property), all its
members are accessible via this function or property:
class C {
private fun getObject() = object {
val x: String = "x"
}
fun printX() {
println(getObject().x)
283
}
}
If this function or property is public or private inline, its actual type is:
The declared supertype of the anonymous object, if there is exactly one such type
The explicitly declared type if there is more than one declared supertype
In all these cases, members added in the anonymous object are not accessible. Overridden members are accessible if
they are declared in the actual type of the function or property:
interface A {
fun funFromA() {}
}
interface B
class C {
// The return type is Any. x is not accessible
fun getObject() = object {
val x: String = "x"
}
Accessing
variables
from
anonymous
objects
The code in object expressions can access variables from the enclosing scope:
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}
284
// ...
}
Object
declarations
The Singleton pattern can be useful in several cases, and Kotlin makes it easy to declare singletons:
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
This is called an object declaration, and it always has a name following the object keyword. Just like a variable
declaration, an object declaration is not an expression, and it cannot be used on the right-hand side of an assignment
statement.
DataProviderManager.registerDataProvider(...)
Object declarations can't be local (that is, they can't be nested directly inside a function), but they can be
nested into other object declarations or non-inner classes.
Companion
objects
An object declaration inside a class can be marked with the companion keyword:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
285
Members of the companion object can be called simply by using the class name as the qualifier:
The name of the companion object can be omitted, in which case the name Companion will be used:
class MyClass {
companion object { }
}
val x = MyClass.Companion
Class members can access the private members of the corresponding companion object.
The name of a class used by itself (not as a qualifier to another name) acts as a reference to the companion object of the
class (whether named or not):
class MyClass1 {
companion object Named { }
}
val x = MyClass1
class MyClass2 {
companion object { }
}
val y = MyClass2
Note that even though the members of companion objects look like static members in other languages, at runtime those
are still instance members of real objects, and can, for example, implement interfaces:
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
However, on the JVM you can have members of companion objects generated as real static methods and fields if you use
the @JvmStatic annotation. See the Java interoperability section for more detail.
Semantic
difference
between
object
expressions
and
declarations
There is one important semantic difference between object expressions and object declarations:
286
Object expressions are executed (and initialized) immediately, where they are used.
Object declarations are initialized lazily, when accessed for the first time.
A companion object is initialized when the corresponding class is loaded (resolved) that matches the semantics of a
Java static initializer.
Delegation
The Delegation pattern has proven to be a good alternative to implementation inheritance, and Kotlin supports it natively
requiring zero boilerplate code.
A class Derived can implement an interface Base by delegating all of its public members to a specified object:
interface Base {
fun print()
}
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
The by -clause in the supertype list for Derived indicates that b will be stored internally in objects of Derived and the
compiler will generate all the methods of Base that forward to b.
Overriding
a
member
of
an
interface
implemented
by
delegation
Overrides work as you expect: the compiler will use your override implementations instead of those in the delegate
object. If you want to add override fun printMessage() { print("abc") } to Derived, the program would print abc instead of
10 when printMessage is called:
interface Base {
fun printMessage()
fun printMessageLine()
}
287
class Derived(b: Base) : Base by b {
override fun printMessage() { print("abc") }
}
fun main() {
val b = BaseImpl(10)
Derived(b).printMessage()
Derived(b).printMessageLine()
}
Note, however, that members overridden in this way do not get called from the members of the delegate object, which
can only access its own implementations of the interface members:
interface Base {
val message: String
fun print()
}
fun main() {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
}
Delegated
properties
There are certain common kinds of properties, that, though you can implement them manually every time you need them,
it would be helpful to implement them once and add to a library. Examples include:
Lazy properties: the value gets computed only upon first access.
class Example {
var p: String by Delegate()
288
}
The syntax is: val/var <property name>: <Type> by <expression>. The expression after by is a delegate, because get()
(and set()) corresponding to the property will be delegated to its getValue() and setValue() methods. Property delegates
don’t have to implement any interface, but they have to provide a getValue() function (and setValue()--- for var s).
For example:
import kotlin.reflect.KProperty
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
When you read from p that delegate