0% found this document useful (0 votes)
218 views1,455 pages

Kotlin Reference

Uploaded by

rogerio silva
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
218 views1,455 pages

Kotlin Reference

Uploaded by

rogerio silva
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 1455

Kotlin Language Documentation 2.0.

0
Table of Contents

Kotlin Docs 66

Get started with Kotlin 66

Install Kotlin 66

Choose your Kotlin use case 66

Is anything missing? 68

Welcome to our tour of Kotlin! 68

Hello world 68

Variables 69

String templates 69

Practice 69

Next step 70

Basic types 70

Practice 71

Next step 71

Collections 71

List 72

Set 73

Map 74

Practice 76

Next step 77

Control flow 77

Conditional expressions 77

Ranges 79

Loops 79

Practice 80

Next step 82

2
Functions
82
Named arguments 83

Default parameter values 83

Functions without return 83

Single-expression functions 84

Functions practice 84

Lambda expressions 85

Lambda expressions practice 88

Next step 88

Classes 88

Properties 89

Create instance 89

Access properties 90

Member functions 90

Data classes 90

Practice 92

Next step 93

Null safety 93

Nullable types 93

Check for null values 94

Use safe calls 94

Use Elvis operator 94

Practice 95

What's next? 95

Kotlin Multiplatform 95

Kotlin Multiplatform use cases 95

Code sharing between platforms 96

Get started 96

Kotlin for server side 97

3
Frameworks for server-side development with Kotlin 97

Deploying Kotlin server-side applications 97

Products that use Kotlin on the server side 98

Next steps 98

Kotlin for Android 98

Kotlin Wasm 98

Kotlin/Wasm performance 99

Browser API support 100

Leave feedback 100

Learn more 100

Kotlin Native 101

Why Kotlin/Native? 101

Target platforms 101

Interoperability 101

Sharing code between platforms 101

How to get started 102

Kotlin for JavaScript 102

Kotlin/JS IR compiler 102

Kotlin/JS frameworks 102

Join the Kotlin/JS community 103

Kotlin for data analysis 103

Notebooks 104

Kotlin DataFrame 105

Kandy 106

What's next 107

Kotlin for competitive programming 107

Simple example: Reachable Numbers problem 108

4
Functional operators example: Long Number problem 109

More tips and tricks 110

Learning Kotlin 110

What's new in Kotlin 2.0.0 111

IDE support 111

Kotlin K2 compiler 111

Kotlin/JVM 119

Kotlin/Native 120

Kotlin/Wasm 121

Kotlin/JS 122

Gradle improvements 126

Standard library 133

Install Kotlin 2.0.0 135

What's new in Kotlin 2.0.0-RC3 135

IDE support 135

Kotlin K2 compiler 135

Kotlin/JVM 142

Kotlin/Native 143

Kotlin/Wasm 143

Kotlin/JS 145

Gradle improvements 148

Standard library 154

What to expect from upcoming Kotlin EAP releases 156

How to update to Kotlin 2.0.0-RC3 156

What's new in Kotlin 1.9.20 156

IDE support 157

New Kotlin K2 compiler updates 157

Kotlin/JVM 158

Kotlin/Native 158

5
Kotlin Multiplatform 162

Kotlin/Wasm 170

Gradle 171

Standard library 172

Documentation updates 173

Install Kotlin 1.9.20 174

What's new in Kotlin 1.9.0 174

IDE support 175

New Kotlin K2 compiler updates 175

Language 177

Kotlin/JVM 178

Kotlin/Native 179

Kotlin Multiplatform 181

Kotlin/Wasm 182

Kotlin/JS 184

Gradle 185

Standard library 188

Documentation updates 193

Install Kotlin 1.9.0 193

Compatibility guide for Kotlin 1.9.0 193

What's new in Kotlin 1.8.20 194

IDE support 194

New Kotlin K2 compiler updates 194

Language 195

New Kotlin/Wasm target 199

Kotlin/JVM 200

Kotlin/Native 201

Kotlin Multiplatform 203

Kotlin/JavaScript 206

6
Gradle 207

Standard library 210

Serialization updates 212

Documentation updates 213

Install Kotlin 1.8.20 213

What's new in Kotlin 1.8.0 214

IDE support 214

Kotlin/JVM 214

Kotlin/Native 215

Kotlin Multiplatform: A new Android source set layout 216

Kotlin/JS 218

Gradle 220

Standard library 222

Documentation updates 225

Install Kotlin 1.8.0 225

Compatibility guide for Kotlin 1.8.0 226

What's new in Kotlin 1.7.20 226

Support for Kotlin K2 compiler plugins 226

Language 227

Kotlin/JVM 231

Kotlin/Native 233

Kotlin/JS 234

Gradle 234

Standard library 235

Documentation updates 237

Install Kotlin 1.7.20 237

What's new in Kotlin 1.7.0 238

New Kotlin K2 compiler for the JVM in Alpha 238

7
Language 239

Kotlin/JVM 241

Kotlin/Native 241

Kotlin/JS 243

Standard library 244

Gradle 248

Migrating to Kotlin 1.7.0 252

What's new in Kotlin 1.6.20 252

Language 253

Kotlin/JVM 254

Kotlin/Native 256

Kotlin Multiplatform 259

Kotlin/JS 261

Security 262

Gradle 264

What's new in Kotlin 1.6.0 265

Language 265

Supporting previous API versions for a longer period 268

Kotlin/JVM 268

Kotlin/Native 269

Kotlin/JS 271

Kotlin Gradle plugin 272

Standard library 272

Tools 275

Coroutines 1.6.0-RC 276

Migrating to Kotlin 1.6.0 276

What's new in Kotlin 1.5.30 276

Language features 277

Kotlin/JVM 280

8
Kotlin/Native 281

Kotlin Multiplatform 283

Kotlin/JS 285

Gradle 285

Standard library 288

Serialization 1.3.0-RC 290

What's new in Kotlin 1.5.20 291

Kotlin/JVM 291

Kotlin/Native 292

Kotlin/JS 293

Gradle 294

Standard library 294

What's new in Kotlin 1.5.0 295

Language features 295

Kotlin/JVM 297

Kotlin/Native 299

Kotlin/JS 300

Kotlin Multiplatform 300

Standard library 300

kotlin-test library 304

kotlinx libraries 307

Migrating to Kotlin 1.5.0 308

What's new in Kotlin 1.4.30 308

Language features 308

Kotlin/JVM 311

Kotlin/Native 311

Kotlin/JS 312

Gradle project improvements 312

9
Standard library 312

Serialization updates 314

What's new in Kotlin 1.4.20 314

Kotlin/JVM 314

Kotlin/JS 315

Kotlin/Native 317

Kotlin Multiplatform 318

Standard library 319

Kotlin Android Extensions 319

What's new in Kotlin 1.4.0 320

Language features and improvements 320

New tools in the IDE 323

New compiler 326

Kotlin/JVM 328

Kotlin/JS 330

Kotlin/Native 330

Kotlin Multiplatform 332

Gradle project improvements 335

Standard library 336

Stable JSON serialization 342

Scripting and REPL 342

Migrating to Kotlin 1.4.0 343

What's new in Kotlin 1.3 343

Coroutines release 343

Kotlin/Native 344

Multiplatform projects 344

Contracts 344

Capturing when subject in a variable 345

@JvmStatic and @JvmField in companions of interfaces 345

10
Nested declarations in annotation classes 346

Parameterless main 346

Functions with big arity 346

Progressive mode 347

Inline classes 347

Unsigned integers 347

@JvmDefault 348

Standard library 348

Tooling 350

What's new in Kotlin 1.2 350

Table of contents 350

Multiplatform projects (experimental) 351

Other language features 351

Standard library 354

JVM backend 356

JavaScript backend 356

Tools 356

What's new in Kotlin 1.1 357

Table of contents 357

JavaScript 357

Coroutines (experimental) 357

Other language features 358

Standard library 362

JVM Backend 365

JavaScript backend 365

Kotlin releases 366

Update to a new release 366

IDE support 367

11
Kotlin release compatibility 367

Release details 367

Kotlin roadmap 374

Key priorities 375

Kotlin roadmap by subsystem 375

What's changed since July 2023 376

Basic syntax 377

Package definition and imports 377

Program entry point 378

Print to the standard output 378

Functions 378

Variables 379

Creating classes and instances 380

Comments 380

String templates 381

Conditional expressions 381

for loop 381

while loop 382

when expression 382

Ranges 382

Collections 383

Nullable values and null checks 384

Type checks and automatic casts 385

Idioms 386

Create DTOs (POJOs/POCOs) 386

Default values for function parameters 386

Filter a list 386

Check the presence of an element in a collection 386

String interpolation 386

12
Instance checks 387

Read-only list 387

Read-only map 387

Access a map entry 387

Traverse a map or a list of pairs 387

Iterate over a range 387

Lazy property 387

Extension functions 387

Create a singleton 388

Use inline value classes for type-safe values 388

Instantiate an abstract class 388

If-not-null shorthand 388

If-not-null-else shorthand 388

Execute a statement if null 389

Get first item of a possibly empty collection 389

Execute if not null 389

Map nullable value if not null 389

Return on when statement 389

try-catch expression 389

if expression 389

Builder-style usage of methods that return Unit 390

Single-expression functions 390

Call multiple methods on an object instance (with) 390

Configure properties of an object (apply) 390

Java 7's try-with-resources 391

Generic function that requires the generic type information 391

Swap two variables 391

Mark code as incomplete (TODO) 391

What's next? 391

13
Coding conventions 391

Configure style in IDE 391

Source code organization 392

Naming rules 393

Formatting 395

Documentation comments 402

Avoid redundant constructs 403

Idiomatic use of language features 403

Coding conventions for libraries 406

Basic types 407

Numbers 407

Integer types 407

Floating-point types 408

Literal constants for numbers 408

Numbers representation on the JVM 409

Explicit number conversions 409

Operations on numbers 410

Unsigned integer types 412

Unsigned arrays and ranges 412

Unsigned integers literals 412

Use cases 413

Booleans 413

Characters 414

Strings 414

String literals 415

String templates 416

String formatting 416

14
Arrays 417

When to use arrays 417

Create arrays 418

Access and modify elements 419

Work with arrays 419

Primitive-type arrays 421

What's next? 422

Type checks and casts 422

is and !is operators 423

Smart casts 423

"Unsafe" cast operator 425

"Safe" (nullable) cast operator 426

Conditions and loops 426

If expression 426

When expression 426

For loops 428

While loops 429

Break and continue in loops 429

Returns and jumps 429

Break and continue labels 429

Return to labels 430

Exceptions 431

Exception classes 431

Checked exceptions 432

The Nothing type 432

Java interoperability 433

Packages and imports 433

Default imports 433

15
Imports 434

Visibility of top-level declarations 434

Classes 434

Constructors 434

Creating instances of classes 436

Class members 436

Inheritance 437

Abstract classes 437

Companion objects 437

Inheritance 437

Overriding methods 438

Overriding properties 438

Derived class initialization order 439

Calling the superclass implementation 439

Overriding rules 440

Properties 440

Declaring properties 440

Getters and setters 440

Compile-time constants 442

Late-initialized properties and variables 442

Overriding properties 443

Delegated properties 443

Interfaces 443

Implementing interfaces 443

Properties in interfaces 443

Interfaces Inheritance 444

Resolving overriding conflicts 444

Functional (SAM) interfaces 445

16
SAM conversions 445

Migration from an interface with constructor function to a functional interface 445

Functional interfaces vs. type aliases 446

Visibility modifiers 446

Packages 446

Class members 447

Modules 448

Extensions 448

Extension functions 448

Extensions are resolved statically 449

Nullable receiver 450

Extension properties 450

Companion object extensions 450

Scope of extensions 450

Declaring extensions as members 451

Note on visibility 452

Data classes 452

Properties declared in the class body 452

Copying 453

Data classes and destructuring declarations 453

Standard data classes 453

Sealed classes and interfaces 453

Declare a sealed class or interface 454

Inheritance 456

Use sealed classes with when expression 456

Use case scenarios 457

Generics: in, out, where 459

Variance 459

17
Type projections 461
Generic functions 462

Generic constraints 462

Definitely non-nullable types 463

Type erasure 463

Underscore operator for type arguments 465

Nested and inner classes 465

Inner classes 466

Anonymous inner classes 466

Enum classes 466

Anonymous classes 466

Implementing interfaces in enum classes 467

Working with enum constants 467

Inline value classes 468

Members 468

Inheritance 469

Representation 469

Inline classes vs type aliases 470

Inline classes and delegation 471

Object expressions and declarations 471

Object expressions 471

Object declarations 473

Delegation 476

Overriding a member of an interface implemented by delegation 476

Delegated properties 477

Standard delegates 478

Delegating to another property 478

Storing properties in a map 479

18
Local delegated properties 480

Property delegate requirements 480

Translation rules for delegated properties 481

Providing a delegate 482

Type aliases 483

Functions 484

Function usage 484

Function scope 488

Generic functions 488

Tail recursive functions 489

Higher-order functions and lambdas 489

Higher-order functions 489

Function types 490

Lambda expressions and anonymous functions 492

Inline functions 494

noinline 495

Non-local returns 495

Reified type parameters 496

Inline properties 496

Restrictions for public API inline functions 497

Operator overloading 497

Unary operations 497

Binary operations 499

Infix calls for named functions 502

Type-safe builders 502

How it works 502

Scope control: @DslMarker 504

Full definition of the com.example.html package 505

19
Using builders with builder type inference 506

Writing your own builders 506

How builder inference works 508

Null safety 510

Nullable types and non-nullable types 510

Checking for null in conditions 511

Safe calls 511

Nullable receiver 511

Elvis operator 512

The !! operator 512

Safe casts 512

Collections of a nullable type 512

What's next? 513

Equality 513

Structural equality 513

Referential equality 514

Floating-point numbers equality 514

Array equality 514

This expressions 514

Qualified this 515

Implicit this 515

Asynchronous programming techniques 515

Threading 516

Callbacks 516

Futures, promises, and others 516

Reactive extensions 517

Coroutines 517

Coroutines 518

20
How to start 518

Sample projects 519

Annotations 519

Usage 519

Constructors 519

Instantiation 520

Lambdas 520

Annotation use-site targets 520

Java annotations 521

Repeatable annotations 523

Destructuring declarations 523

Example: returning two values from a function 524

Example: destructuring declarations and maps 524

Underscore for unused variables 524

Destructuring in lambdas 524

Reflection 525

JVM dependency 525

Class references 526

Callable references 526

Introduction to Kotlin Multiplatform 529

Start from scratch 529

Dive deep into Kotlin Multiplatform 530

Get help 530

The basics of Kotlin Multiplatform project structure 530

Common code 530

Targets 531

Source sets 533

Integration with tests 537

21
What's next? 538

Advanced concepts of the multiplatform project structure 538

Dependencies and dependsOn 538

Declaring custom source sets 542

Compilations 544

Set up targets for Kotlin Multiplatform 545

Distinguish several targets for one platform 545

Share code on platforms 546

Share code on all platforms 546

Share code on similar platforms 546

Share code in libraries 547

Connect platform-specific libraries 547

What's next? 547

Expected and actual declarations 547

Rules for expected and actual declarations 548

Different approaches for using expected and actual declarations 548

Advanced use cases 553

What's next? 554

Hierarchical project structure 555

Default hierarchy template 555

Manual configuration 559

Adding dependencies on multiplatform libraries 561

Dependency on a Kotlin library 561

Dependency on Kotlin Multiplatform libraries 563

Dependency on another multiplatform project 564

What's next? 564

Adding Android dependencies 565

22
What's next? 565

Adding iOS dependencies 566

With CocoaPods 566

Without CocoaPods 567

What's next? 569

Configure compilations 569

Configure all compilations 570

Configure compilations for one target 570

Configure one compilation 571

Create a custom compilation 571

Use Java sources in JVM compilations 572

Configure interop with native languages 573

Compilation for Android 574

Compilation of the source set hierarchy 575

Build final native binaries (Experimental DSL) 575

Declare binaries 576

Configure binaries 577

Build final native binaries 580

Declare binaries 581

Access binaries 582

Export dependencies to binaries 583

Build universal frameworks 585

Build XCFrameworks 585

Customize the Info.plist file 586

Publishing multiplatform libraries 587

Structure of publications 587

Host requirements 588

Publish an Android library 589

23
Disable sources publication 589

Disable JVM environment attribute publication 590

Multiplatform Gradle DSL reference 590

Id and version 590

Top-level blocks 590

Targets 591

Source sets 597

Compilations 599

Dependencies 602

Language settings 603

Android source set layout 604

Check the compatibility 604

Rename Kotlin source sets 604

Move source files 604

Move the AndroidManifest.xml file 605

Check the relationship between Android and common tests 605

Adjust the implementation of Android flavors 606

Compatibility guide for Kotlin Multiplatform 606

Version compatibility 606

New approach to auto-generated targets 606

Changes in Gradle input and output compile tasks 607

New configuration names for dependencies on the compilation 607

Deprecated Gradle properties for hierarchical structure support 608

Deprecated support of multiplatform libraries published in the legacy mode 609

Deprecated API for adding Kotlin source sets directly to the Kotlin compilation 609

Migration from kotlin-js Gradle plugin to kotlin-multiplatform Gradle plugin 610

Rename of android target to androidTarget 612

Declaring several similar targets 612

Deprecated jvmWithJava preset 614

24
Deprecated legacy Android source set layout 614

Deprecated commonMain and commonTest with custom dependsOn 615

Deprecated target presets API 615

New approach to forward declarations 616

Kotlin Multiplatform Mobile plugin releases 617

Update to the new release 617

Release details 617

Get started with Kotlin Notebook 622

Next step 622

Set up an environment 622

Set up the environment 622

Next step 623

Create your first Kotlin Notebook 623

Create an empty project 623

Create a Kotlin Notebook 625

Next step 626

Add dependencies to your Kotlin Notebook 626

Add Kotlin DataFrame and Kandy libraries to your Kotlin Notebook 627

Next step 629

Share your Kotlin Notebook 629

Share a Kotlin Notebook 630

What's next 631

Output formats supported by Kotlin Notebook 631

Texts 632

HTML 634

Images 634

Math formulas and equations 636

25
Data frames 636

Charts 637

What's next 638

Retrieve data from files 638

Before you start 638

Retrieve data from a file 639

Display data 639

Refine data 640

Save DataFrame 641

What's next 642

Retrieve data from web sources and APIs 642

Before you start 642

Fetch data from an API 642

Clean and refine data 643

Analyze data in Kotlin Notebook 644

What's next 646

Connect and retrieve data from databases 646

Before you start 646

Connect to database 647

Retrieve and manipulate data 647

Analyze data in Kotlin Notebook 648

What's next 648

Data visualization in Kotlin Notebook with Kandy 649

Before you start 649

Create the DataFrame 649

Create a line chart 650

Create a points chart 651

Create a bar chart 652

26
What's next 653

Kotlin and Java libraries for data analysis 653

Kotlin libraries 653

Java libraries 654

Get started with Kotlin/JVM 656

Create a project 656

Create an application 658

Run the application 659

What's next? 660

Comparison to Java 660

Some Java issues addressed in Kotlin 660

What Java has that Kotlin does not 660

What Kotlin has that Java does not 661

What's next? 661

Calling Java from Kotlin 661

Getters and setters 662

Java synthetic property references 662

Methods returning void 663

Escaping for Java identifiers that are keywords in Kotlin 663

Null-safety and platform types 663

Mapped types 668

Java generics in Kotlin 670

Java arrays 670

Java varargs 671

Operators 671

Checked exceptions 672

Object methods 672

Inheritance from Java classes 673

27
Accessing static members 673

Java reflection 673

SAM conversions 673

Using JNI with Kotlin 673

Using Lombok-generated declarations in Kotlin 674

Calling Kotlin from Java 674

Properties 674

Package-level functions 674

Instance fields 675

Static fields 676

Static methods 676

Default methods in interfaces 677

Visibility 679

KClass 679

Handling signature clashes with @JvmName 679

Overloads generation 680

Checked exceptions 680

Null-safety 681

Variant generics 681

Get started with Spring Boot and Kotlin 682

Next step 682

Join the community 682

Create a Spring Boot project with Kotlin 682

Before you start 682

Create a Spring Boot project 683

Explore the project Gradle build file 686

Explore the generated Spring Boot application 687

Create a controller 688

Run the application 689

28
Next step 690

Add a data class to Spring Boot project 690

Update your application 690

Run the application 692

Next step 692

Add database support for Spring Boot project 692

Add database support 692

Update the MessageController class 693

Update the MessageService class 694

Configure the database 694

Add messages to database via HTTP request 695

Retrieve messages by id 697

Run the application 698

Next step 699

Use Spring Data CrudRepository for database access 699

Update your application 700

Run the application 701

What's next 701

Test code using JUnit in JVM – tutorial 701

Add dependencies 701

Add the code to test it 702

Create a test 702

Run a test 703

What's next 704

Mixing Java and Kotlin in one project – tutorial 705

Adding Java source code to an existing Kotlin project 705

Adding Kotlin source code to an existing Java project 706

Converting an existing Java file to Kotlin with J2K 707

29
Using Java records in Kotlin 707

Using Java records from Kotlin code 708

Declare records in Kotlin 708

Further discussion 708

Strings in Java and Kotlin 709

Concatenate strings 709

Build a string 709

Create a string from collection items 709

Set default value if the string is blank 710

Replace characters at the beginning and end of a string 710

Replace occurrences 711

Split a string 711

Take a substring 711

Use multiline strings 712

What's next? 713

Collections in Java and Kotlin 713

Operations that are the same in Java and Kotlin 713

Operations that differ a bit 715

Operations that don't exist in Java's standard library 716

Mutability 717

Covariance 718

Ranges and progressions 718

Comparison by several criteria 719

Sequences 720

Removal of elements from a list 720

Traverse a map 721

Get the first and the last items of a possibly empty collection 721

Create a set from a list 721

Group elements 722

30
Filter elements 722

Collection transformation operations 723

What's next? 724

Nullability in Java and Kotlin 724

Support for nullable types 725

Platform types 726

Support for definitely non-nullable types 726

Checking the result of a function call 727

Default values instead of null 727

Functions returning a value or null 728

Aggregate operations 728

Casting types safely 728

What's next? 729

Get started with Kotlin/Native in IntelliJ IDEA 729

Before you start 729

Build and run the application 730

Update the application 731

What's next? 733

Get started with Kotlin/Native using Gradle 733

Create project files 734

Build and run the application 735

Open the project in an IDE 735

What's next? 735

Get started with Kotlin/Native using the command-line compiler 735

Obtain the compiler 735

Write "Hello Kotlin/Native" program 735

Compile the code from the console 736

Interoperability with C 736

31
Platform libraries 736

Simple example 736

Create bindings for a new library 737

Bindings 739

Mapping primitive data types from C – tutorial 743

Types in C language 743

Example C library 743

Inspect generated Kotlin APIs for a C library 744

Primitive types in kotlin 745

Fix the code 746

Next steps 746

Mapping struct and union types from C – tutorial 747

Mapping struct and union C types 747

Inspect Generated Kotlin APIs for a C library 747

Struct and union types in Kotlin 749

Use struct and union types from Kotlin 749

Run the code 751

Next steps 751

Mapping function pointers from C – tutorial 752

Mapping function pointer types from C 752

Inspect generated Kotlin APIs for a C library 752

C function pointers in Kotlin 754

Pass Kotlin function as C function pointer 754

Use the C function pointer from Kotlin 754

Fix the code 754

Next Steps 755

Mapping Strings from C – tutorial 755

Working with C strings 755

32
Inspect generated Kotlin APIs for a C library 756

Strings in Kotlin 757

Pass Kotlin string to C 757

Read C Strings in Kotlin 758

Receive C string bytes from Kotlin 758

Fix the Code 758

Next steps 759

Create an app using C Interop and libcurl – tutorial 759

Before you start 759

Create a definition file 760

Add interoperability to the build process 761

Write the application code 762

Compile and run the application 762

Interoperability with Swift/Objective-C 763

Usage 763

Mappings 764

Casting between mapped types 771

Subclassing 771

C features 771

Export of KDoc comments to generated Objective-C headers 771

Unsupported 772

Kotlin/Native as an Apple framework – tutorial 773

Create a Kotlin library 773

Generated framework headers 775

Garbage collection and reference counting 778

Use the code from Objective-C 778

Use the code from Swift 778

Xcode and framework dependencies 779

Next steps 779

33
CocoaPods overview and setup 779

Set up an environment to work with CocoaPods 780

Add and configure Kotlin CocoaPods Gradle plugin 781

Update Podfile for Xcode 782

Possible issues and solutions 782

Add dependencies on a Pod library 783

From the CocoaPods repository 784

On a locally stored library 784

From a custom Git repository 785

From a custom Podspec repository 786

With custom cinterop options 786

Use a Kotlin Gradle project as a CocoaPods dependency 788

Xcode project with one target 788

Xcode project with several targets 789

CocoaPods Gradle plugin DSL reference 789

Enable the plugin 790

cocoapods block 790

pod() function 792

Swift package export setup 793

Prepare file locations 793

Create the XCFramework and the Swift package manifest 794

Exporting multiple modules as an XCFramework 795

Kotlin/Native libraries 796

Kotlin compiler specifics 796

cinterop tool specifics 796

klib utility 796

Several examples 797

34
Advanced topics 798

Platform libraries 799

POSIX bindings 799

Popular native libraries 799

Availability by default 799

Kotlin/Native as a dynamic library – tutorial 799

Create a Kotlin library 800

Generated headers file 801

Use generated headers from C 804

Compile and run the example on Linux and macOS 805

Compile and run the example on Windows 805

Next steps 805

Kotlin/Native memory management 806

Garbage collector 806

Memory consumption 807

Unit tests in the background 808

What's next 809

iOS integration 809

Threads 809

Garbage collection and lifecycle 810

Support for background state and App Extensions 812

Migrate to the new memory manager 812

Update Kotlin 813

Update dependencies 813

Update your code 813

Support both new and legacy memory managers 814

What's next 814

Debugging Kotlin/Native 814

35
Produce binaries with debug info with Kotlin/Native compiler 815

Breakpoints 815

Stepping 816

Variable inspection 816

Known issues 817

Symbolicating iOS crash reports 817

Producing .dSYM for release Kotlin binaries 818

Make frameworks static when using rebuild from bitcode 818

Kotlin/Native target support 818

Tier 1 819

Tier 2 819

Tier 3 820

For library authors 821

Privacy manifest for iOS apps 821

What's the issue 821

How to resolve 821

Tips for improving Kotlin/Native compilation times 822

General recommendations 822

Gradle configuration 822

Windows OS configuration 823

License files for the Kotlin/Native binaries 823

Kotlin/Native FAQ 824

How do I run my program? 824

What is Kotlin/Native memory management model? 824

How do I create a shared library? 824

How do I create a static library or an object file? 825

How do I run Kotlin/Native behind a corporate proxy? 825

36
How do I specify a custom Objective-C prefix/name for my Kotlin framework? 825

How do I rename the iOS framework? 825

How do I enable bitcode for my Kotlin framework? 826

Why do I see InvalidMutabilityException? 826

How do I make a singleton object mutable? 826

How can I compile my project with unreleased versions of Kotlin/Native? 826

Get started with Kotlin/Wasm in IntelliJ IDEA 826

Before you start 827

Open the project in IntelliJ IDEA 828

Run the application 828

Generate artifacts 830

Publish on GitHub pages 832

What's next? 833

Debug Kotlin/Wasm code 833

Before you start 833

Open the project in IntelliJ IDEA 835

Run the application 835

Debug in your browser 837

Leave feedback 840

What's next? 840

Interoperability with JavaScript 841

Use JavaScript code in Kotlin 841

Use Kotlin code in JavaScript 844

Type correspondence 844

Exception handling 846

Kotlin/Wasm and Kotlin/JS interoperability differences 846

Troubleshooting 847

Browser versions 848

37
Wasm proposals support 848

Set up a Kotlin/JS project 849

Execution environments 850

Support for ES2015 features 850

Dependencies 850

run task 852

test task 853

webpack bundling 854

CSS 855

Node.js 856

Yarn 857

Distribution target directory 859

Module name 860

package.json customization 860

Run Kotlin/JS 860

Run the Node.js target 860

Run the browser target 861

Development server and continuous compilation 862

Debug Kotlin/JS code 864

Debug in browser 864

Debug in the IDE 866

Debug in Node.js 869

What's next? 869

If you run into any problems 869

Run tests in Kotlin/JS 869

Kotlin/JS dead code elimination 873

Exclude declarations from DCE 873

Disable DCE 874

38
Kotlin/JS IR compiler 874

Lazy initialization of top-level properties 875

Incremental compilation for development binaries 875

Output mode 875

Ignoring compilation errors 876

Minification of member names in production 876

Preview: generation of TypeScript declaration files (d.ts) 876

Current limitations of the IR compiler 877

Migrating existing projects to the IR compiler 877

Authoring libraries for the IR compiler with backwards compatibility 877

Migrating Kotlin/JS projects to the IR compiler 878

Convert JS- and React-related classes and interfaces to external interfaces 878

Convert properties of external interfaces to var 878

Convert functions with receivers in external interfaces to regular functions 879

Create plain JS objects for interoperability 879

Replace toString() calls on function references with .name 879

Explicitly specify binaries.executable() in the build script 880

Additional troubleshooting tips when working with the Kotlin/JS IR compiler 880

Browser and DOM API 880

Interaction with the DOM 880

Use JavaScript code from Kotlin 881

Inline JavaScript 881

external modifier 881

Equality 884

Dynamic type 884

Use dependencies from npm 885

Use Kotlin code from JavaScript 886

39
Isolating declarations in a separate JavaScript object in plain mode 886

Package structure 886

Kotlin types in JavaScript 887

JavaScript modules 889

Browser targets 889

JavaScript libraries and Node.js files 890

@JsModule annotation 890

Kotlin/JS reflection 892

Class references 892

KType and typeOf() 892

KClass and createInstance() 892

Example 892

Typesafe HTML DSL 893

Build a web application with React and Kotlin/JS — tutorial 894

Before you start 894

Create a web app draft 897

Design app components 902

Compose components 906

Add more components 907

Use packages from npm 909

Use an external REST API 912

Deploy to production and the cloud 915

What's next 916

Get started with Kotlin custom scripting – tutorial 917

Project structure 917

Before you start 917

Create a project 917

Add scripting modules 918

40
Create a script definition 920

Create a scripting host 922

Run scripts 923

What's next? 924

Collections overview 924

Collection types 924

Constructing collections 928

Construct from elements 928

Create with collection builder functions 929

Empty collections 929

Initializer functions for lists 929

Concrete type constructors 929

Copy 930

Invoke functions on other collections 930

Iterators 931

List iterators 932

Mutable iterators 932

Ranges and progressions 933

Progression 933

Sequences 934

Construct 935

Sequence operations 935

Sequence processing example 936

Collection operations overview 937

Extension and member functions 937

Common operations 937

Write operations 938

41
Collection transformation operations 939

Map 939

Zip 939

Associate 940

Flatten 941

String representation 941

Filtering collections 942

Filter by predicate 942

Partition 943

Test predicates 943

Plus and minus operators 944

Grouping 944

Retrieve collection parts 945

Slice 945

Take and drop 945

Chunked 946

Windowed 946

Retrieve single elements 947

Retrieve by position 947

Retrieve by condition 948

Retrieve with selector 948

Random element 949

Check element existence 949

Ordering 949

Natural order 950

Custom orders 950

Reverse order 951

42
Random order 951

Aggregate operations 952

Fold and reduce 952

Collection write operations 954

Adding elements 954

Removing elements 954

Updating elements 955

List-specific operations 955

Retrieve elements by index 955

Retrieve list parts 956

Find element positions 956

List write operations 957

Set-specific operations 959

Map-specific operations 960

Retrieve keys and values 960

Filter 960

Plus and minus operators 961

Map write operations 961

Opt-in requirements 963

Opt in to using API 963

Require opt-in for API 965

Opt-in requirements for pre-stable APIs 966

Scope functions 967

Function selection 967

Distinctions 968

Functions 971

takeIf and takeUnless 973

43
Time measurement 975
Calculate duration 975

Measure time 978

Time sources 979

Coroutines guide 980

Table of contents 980

Additional references 981

Coroutines basics 981

Your first coroutine 981

Extract function refactoring 982

Scope builder 982

Scope builder and concurrency 983

An explicit job 983

Coroutines are light-weight 984

Coroutines and channels − tutorial 984

Before you start 985

Blocking requests 986

Callbacks 989

Suspending functions 993

Coroutines 994

Concurrency 996

Structured concurrency 999

Showing progress 1002

Channels 1004

Testing coroutines 1007

What's next 1010

Cancellation and timeouts 1010

Cancelling coroutine execution 1010

44
Cancellation is cooperative 1011

Making computation code cancellable 1012

Closing resources with finally 1012

Run non-cancellable block 1013

Timeout 1013

Asynchronous timeout and resources 1014

Composing suspending functions 1015

Sequential by default 1016

Concurrent using async 1016

Lazily started async 1017

Async-style functions 1018

Structured concurrency with async 1019

Coroutine context and dispatchers 1020

Dispatchers and threads 1020

Unconfined vs confined dispatcher 1021

Debugging coroutines and threads 1022

Jumping between threads 1023

Job in the context 1024

Children of a coroutine 1024

Parental responsibilities 1025

Naming coroutines for debugging 1025

Combining context elements 1026

Coroutine scope 1026

Asynchronous Flow 1028

Representing multiple values 1028

Flows are cold 1030

Flow cancellation basics 1031

Flow builders 1031

Intermediate flow operators 1032

45
Terminal flow operators 1033

Flows are sequential 1034

Flow context 1035

Buffering 1036

Composing multiple flows 1039

Flattening flows 1040

Flow exceptions 1042

Exception transparency 1043

Flow completion 1045

Imperative versus declarative 1047

Launching flow 1047

Flow and Reactive Streams 1050

Channels 1050

Channel basics 1050

Closing and iteration over channels 1050

Building channel producers 1051

Pipelines 1051

Prime numbers with pipeline 1052

Fan-out 1053

Fan-in 1054

Buffered channels 1055

Channels are fair 1056

Ticker channels 1056

Coroutine exceptions handling 1057

Exception propagation 1057

CoroutineExceptionHandler 1058

Cancellation and exceptions 1059

Exceptions aggregation 1060

Supervision 1061

46
Shared mutable state and concurrency 1063

The problem 1063

Volatiles are of no help 1064

Thread-safe data structures 1064

Thread confinement fine-grained 1065

Thread confinement coarse-grained 1066

Mutual exclusion 1066

Select expression (experimental) 1067

Selecting from channels 1067

Selecting on close 1069

Selecting to send 1070

Selecting deferred values 1071

Switch over a channel of deferred values 1072

Debug coroutines using IntelliJ IDEA – tutorial 1073

Create coroutines 1073

Debug coroutines 1074

Debug Kotlin Flow using IntelliJ IDEA – tutorial 1077

Create a Kotlin flow 1077

Debug the coroutine 1078

Add a concurrently running coroutine 1081

Debug a Kotlin flow with two coroutines 1081

Serialization 1082

Libraries 1082

Formats 1083

Example: JSON serialization 1083

What's next 1084

Lincheck guide 1085

Add Lincheck to your project 1085

47
Add Lincheck to your project 1085

Explore Lincheck 1085

Additional references 1086

Write your first test with Lincheck 1086

Create a project 1086

Add required dependencies 1086

Write a concurrent counter and run the test 1086

Trace the invalid execution 1087

Test the Java standard library 1088

Next step 1089

See also 1089

Stress testing and model checking 1089

Stress testing 1090

Model checking 1091

Which testing strategy is better? 1092

Configure the testing strategy 1092

Scenario minimization 1093

Logging data structure states 1093

Next step 1094

Operation arguments 1094

Next step 1096

Data structure constraints 1096

Next step 1097

Progress guarantees 1097

Next step 1099

Sequential specification 1099

Keywords and operators 1100

Hard keywords 1100

48
Soft keywords
1101
Modifier keywords 1102

Special identifiers 1103

Operators and special symbols 1103

Gradle 1104

What's next? 1104

Get started with Gradle and Kotlin/JVM 1104

Create a project 1104

Explore the build script 1106

Run the application 1107

What's next? 1109

Configure a Gradle project 1109

Apply the plugin 1110

Targeting the JVM 1111

Targeting multiple platforms 1117

Targeting Android 1117

Targeting JavaScript 1117

Triggering configuration actions with the KotlinBasePlugin interface 1118

Configure dependencies 1118

Declare repositories 1124

What's next? 1124

Compiler options in the Kotlin Gradle plugin 1125

How to define options 1125

All compiler options 1127

What's next? 1130

Compilation and caches in the Kotlin Gradle plugin 1130

Incremental compilation 1131

Gradle build cache support 1132

49
Gradle configuration cache support 1133

The Kotlin daemon and how to use it with Gradle 1133

Rolling back to the previous compiler 1134

Defining Kotlin compiler execution strategy 1135

Kotlin compiler fallback strategy 1136

Trying the latest language version 1136

Build reports 1137

What's next? 1138

Support for Gradle plugin variants 1138

Troubleshooting 1139

What's next? 1141

Maven 1141

Configure and enable the plugin 1141

Declare repositories 1141

Set dependencies 1142

Compile Kotlin-only source code 1142

Compile Kotlin and Java sources 1143

Enable incremental compilation 1144

Configure annotation processing 1144

Create JAR file 1144

Create a self-contained JAR file 1144

Specify compiler options 1145

Use BOM 1146

Generate documentation 1146

Enable OSGi support 1146

Ant 1146

Getting the Ant tasks 1146

Targeting JVM with Kotlin-only source 1147

Targeting JVM with Kotlin-only source and multiple roots 1147

50
Targeting JVM with Kotlin and Java source 1147

Targeting JavaScript with single source folder 1147

Targeting JavaScript with Prefix, PostFix and sourcemap options 1148

Targeting JavaScript with single source folder and metaInfo option 1148

References 1148

Introduction 1149

Community 1150

Get started with Dokka 1150

Gradle 1151

Apply Dokka 1151

Generate documentation 1152

Build javadoc.jar 1155

Configuration examples 1156

Configuration options 1160

Maven 1170

Apply Dokka 1171

Generate documentation 1171

Build javadoc.jar 1172

Configuration example 1172

Configuration options 1173

CLI 1178

Get started 1178

Generate documentation 1179

Command line options 1180

JSON configuration 1183

HTML 1190

Generate HTML documentation 1190

51
Configuration 1191

Customization 1193

Markdown 1195

GFM 1195

Jekyll 1196

Javadoc 1197

Generate Javadoc documentation 1198

Dokka plugins 1199

Apply Dokka plugins 1200

Configure Dokka plugins 1201

Notable plugins 1202

Module documentation 1203

File format 1203

Pass files to Dokka 1204

IDEs for Kotlin development 1204

IntelliJ IDEA 1204

Fleet 1204

Android Studio 1204

Eclipse 1205

Compatibility with the Kotlin language versions 1205

Other IDEs support 1205

What's next? 1205

Migrate to Kotlin code style 1205

Kotlin coding conventions and IntelliJ IDEA formatter 1205

Differences between "Kotlin coding conventions" and "IntelliJ IDEA default code style" 1206

Migration to a new code style discussion 1206

Migration to a new code style 1206

Store old code style in project 1207

52
Kotlin Notebook 1208

Data analytics and visualization 1209

Prototyping 1209

Backend development 1210

Code documentation 1211

Sharing code and outputs 1212

What's next 1213

Run code snippets 1213

IDE: scratches and worksheets 1213

Browser: Kotlin Playground 1215

Command line: ki shell 1217

Kotlin and continuous integration with TeamCity 1219

Gradle, Maven, and Ant 1220

IntelliJ IDEA Build System 1220

Other CI servers 1222

Document Kotlin code: KDoc 1222

KDoc syntax 1222

Inline markup 1223

What's next? 1224

Kotlin and OSGi 1224

Maven 1224

Gradle 1224

FAQ 1225

K2 compiler migration guide 1225

Language feature improvements 1226

How to enable the Kotlin K2 compiler 1231

Try the Kotlin K2 compiler in the Kotlin Playground 1232

53
Support in IntelliJ IDEA 1232

How to roll back to the previous compiler 1232

Changes 1232

Compatibility with Kotlin releases 1244

Compiler plugins support 1244

Share your feedback on the new K2 compiler 1245

Kotlin command-line compiler 1245

Install the compiler 1245

Create and run an application 1246

Compile a library 1246

Run the REPL 1246

Run scripts 1247

Kotlin compiler options 1247

Compiler options 1247

Common options 1248

Kotlin/JVM compiler options 1249

Kotlin/JS compiler options 1250

Kotlin/Native compiler options 1251

All-open compiler plugin 1253

Gradle 1253

Maven 1254

Spring support 1255

Command-line compiler 1255

No-arg compiler plugin 1256

In your Kotlin file 1256

Gradle 1256

Maven 1257

JPA support 1257

54
Command-line compiler 1257

SAM-with-receiver compiler plugin 1258

Gradle 1258

Maven 1258

Command-line compiler 1259

kapt compiler plugin 1259

Use in Gradle 1259

Try Kotlin K2 compiler 1260

Annotation processor arguments 1260

Gradle build cache support 1260

Improve the speed of builds that use kapt 1260

Compile avoidance for kapt 1262

Incremental annotation processing 1262

Inherit annotation processors from superconfigurations 1262

Java compiler options 1263

Non-existent type correction 1263

Use in Maven 1263

Use in IntelliJ build system 1264

Use in CLI 1264

Generate Kotlin sources 1265

AP/Javac options encoding 1265

Keep Java compiler's annotation processors 1265

Lombok compiler plugin 1265

Supported annotations 1265

Gradle 1266

Maven 1267

Using with kapt 1267

Command-line compiler 1268

55
Power-assert compiler plugin 1268

Apply the plugin 1268

Configure the plugin 1268

Use the plugin 1269

What's next 1272

Kotlin Symbol Processing API 1272

Overview 1272

How KSP looks at source files 1273

SymbolProcessorProvider: the entry point 1274

Resources 1274

Supported libraries 1275

KSP quickstart 1276

Add a processor 1276

Create a processor of your own 1277

Use your own processor in a project 1278

Pass options to processors 1279

Make IDE aware of generated code 1280

Why KSP 1281

KSP makes creating lightweight compiler plugins easier 1281

Comparison to kotlinc compiler plugins 1281

Comparison to reflection 1281

Comparison to kapt 1282

Limitations 1282

KSP examples 1282

Get all member functions 1282

Check whether a class or function is local 1282

Find the actual class or interface declaration that the type alias points to 1282

Collect suppressed names in a file annotation 1283

56
How KSP models Kotlin code 1283

Type and resolution 1283

Java annotation processing to KSP reference 1284

Program elements 1284

Types 1284

Misc 1285

Details 1286

Incremental processing 1293

Aggregating vs Isolating 1293

Example 1 1294

Example 2 1294

How file dirtiness is determined 1295

Reporting bugs 1295

Multiple round processing 1295

Changes to your processor 1295

Multiple round behavior 1295

Advanced 1296

KSP with Kotlin Multiplatform 1297

Compilation and processing 1297

Avoid the ksp(...) configuration on KSP 1.0.1+ 1297

Running KSP from command line 1298

KSP FAQ 1298

Why KSP? 1298

Why is KSP faster than kapt? 1299

Is KSP Kotlin-specific? 1299

How to upgrade KSP? 1299

Can I use a newer KSP implementation with an older Kotlin compiler? 1299

57
How often do you update KSP? 1299

Besides Kotlin, are there other version requirements to libraries? 1299

What is KSP's future roadmap? 1300

Learning materials overview 1300

Kotlin Koans 1300

Kotlin hands-on 1301

Building Reactive Spring Boot applications with Kotlin coroutines and RSocket 1301

Building web applications with React and Kotlin/JS 1301

Building web applications with Spring Boot and Kotlin 1301

Creating HTTP APIs with Ktor 1301

Creating a WebSocket chat with Ktor 1301

Creating an interactive website with Ktor 1301

Introduction to Kotlin coroutines and channels 1301

Introduction to Kotlin/Native 1301

Kotlin Multiplatform: networking and data storage 1302

Targeting iOS and Android with Kotlin Multiplatform 1302

Kotlin tips 1302

null + null in Kotlin 1302

Deduplicating collection items 1302

The suspend and inline mystery 1303

Unshadowing declarations with their fully qualified name 1303

Return and throw with the Elvis operator 1304

Destructuring declarations 1304

Operator functions with nullable values 1305

Timing code 1305

Improving loops 1306

Strings 1306

Doing more with the Elvis operator 1307

58
Kotlin collections 1307

What's next? 1308

Kotlin books 1308

Advent of Code puzzles in idiomatic Kotlin 1311

Get ready for Advent of Code 1311

Advent of Code 2022 1312

Advent of Code 2021 1314

Advent of Code 2020 1314

What's next? 1316

Learning Kotlin with JetBrains Academy plugin 1316

Teaching Kotlin with JetBrains Academy plugin 1316

Introduction 1316

What's next 1317

Minimizing mental complexity 1317

Simplicity 1317

Readability 1318

Consistency 1321

Predictability 1322

Debuggability 1324

Testability 1326

What's next 1327

Backward compatibility 1327

Compatibility types 1327

Use the Binary compatibility validator 1328

Specify return types explicitly 1328

Avoid adding arguments to existing API functions 1328

Avoid widening or narrowing return types 1329

59
Avoid using data classes in your API 1330

Considerations for using the PublishedApi annotation 1330

Evolve APIs pragmatically 1331

Use the RequiresOptIn mechanism 1331

What's next 1331

Informative documentation 1331

Provide comprehensive documentation 1332

Create personas for your users 1332

Document by example whenever possible 1332

Thoroughly document your API 1333

Document lambda parameters 1333

Use explicit links in documentation 1333

Be self-contained where possible 1334

Use simple English 1334

What's next 1334

Participate in the Kotlin Early Access Preview 1334

How the EAP can help you be more productive with Kotlin 1334

Build details 1335

Install the EAP Plugin for IntelliJ IDEA or Android Studio 1335

If you run into any problems 1337

Configure your build for EAP 1337

Configure in Gradle 1337

Configure in Maven 1338

FAQ 1339

What is Kotlin? 1339

What is the current version of Kotlin? 1339

Is Kotlin free? 1339

Is Kotlin an object-oriented language or a functional one? 1339

60
What advantages does Kotlin give me over the Java programming language? 1339

Is Kotlin compatible with the Java programming language? 1340

What can I use Kotlin for? 1340

Can I use Kotlin for Android development? 1340

Can I use Kotlin for server-side development? 1340

Can I use Kotlin for web development? 1340

Can I use Kotlin for desktop development? 1340

Can I use Kotlin for native development? 1340

What IDEs support Kotlin? 1340

What build tools support Kotlin? 1340

What does Kotlin compile down to? 1340

Which versions of JVM does Kotlin target? 1341

Is Kotlin hard? 1341

What companies are using Kotlin? 1341

Who develops Kotlin? 1341

Where can I learn more about Kotlin? 1341

Are there any books on Kotlin? 1341

Are any online courses available for Kotlin? 1341

Does Kotlin have a community? 1341

Are there Kotlin events? 1342

Is there a Kotlin conference? 1342

Is Kotlin on social media? 1342

Any other online Kotlin resources? 1342

Where can I get an HD Kotlin logo? 1342

Kotlin Evolution 1342

Principles of Pragmatic Evolution 1342

Incompatible changes 1343

Decision making 1343

Language and tooling releases 1344

61
Libraries 1344

Compiler options 1345

Compatibility tools 1345

Stability of Kotlin components 1345

Stability levels explained 1346

GitHub badges for Kotlin components 1346

Stability of subcomponents 1346

Current stability of Kotlin components 1347

Stability of Kotlin components (pre 1.4) 1348

Compatibility guide for Kotlin 2.0 1349

Basic terms 1350

Language 1350

Tools 1352

Compatibility guide for Kotlin 1.9 1355

Basic terms 1355

Language 1355

Standard library 1362

Tools 1364

Compatibility guide for Kotlin 1.8 1365

Basic terms 1365

Language 1365

Standard library 1374

Tools 1375

Compatibility guide for Kotlin 1.7.20 1376

Basic terms 1377

Language 1377

Compatibility guide for Kotlin 1.7 1377

62
Basic terms 1378

Language 1378

Standard library 1382

Tools 1384

Compatibility guide for Kotlin 1.6 1387

Basic terms 1387

Language 1387

Standard library 1392

Tools 1395

Compatibility guide for Kotlin 1.5 1398

Basic terms 1398

Language and stdlib 1398

Tools 1405

Compatibility guide for Kotlin 1.4 1406

Basic terms 1406

Language and stdlib 1406

Tools 1420

Compatibility guide for Kotlin 1.3 1421

Basic terms 1421

Incompatible changes 1421

Compatibility modes 1429

Google Summer of Code with Kotlin 2024 1429

Kotlin contributor guidelines for Google Summer of Code (GSoC) 1430

Project ideas 1430

Google Summer of Code with Kotlin 2023 1433

Project ideas 1433

Security 1436

63
Kotlin documentation as PDF 1437

Contribution 1437

Participate in Early Access Preview 1437

Contribute to the compiler and standard library 1437

Contribute to the Kotlin IDE plugin 1437

Contribute to other Kotlin libraries and tools 1437

Contribute to the documentation 1438

Translate documentation to other languages 1438

Hold events and presentations 1438

KUG guidelines 1438

How to run a KUG? 1438

Support for KUGs from JetBrains 1439

Support from JetBrains for other tech communities 1439

Kotlin Night guidelines 1439

Event guidelines 1439

Event requirements 1439

JetBrains support 1440

Kotlin brand assets 1440

Kotlin Logo 1440

Kotlin mascot 1441

Kotlin User Group brand assets 1442

Kotlin Night brand assets 1445

Testing page 1448

Synchronized tabs 1448

Sections 1448

Codeblocks 1449

Tables 1449

Lists 1451

64
Text elements 1452

Variables 1452

Embedded elements 1452

Notes 1455

65
Kotlin Docs
Get started with Kotlin
Kotlin is a modern but already mature programming language designed 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.

To start, why not take our tour of Kotlin? This tour covers the fundamentals of the Kotlin programming language.

Start the Kotlin tour

Install Kotlin
Kotlin is included in each IntelliJ IDEA and Android Studio release. Download and install one of these IDEs to start using Kotlin.

Choose your Kotlin use case

Backend

Here is how you can take the first steps in developing Kotlin server-side applications.

1. Create your first backend application:

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 that is used by millions of A lightweight framework for those who value freedom in making architectural decisions.
developers worldwide.
Create HTTP APIs with Ktor.
Create a RESTful web service with Spring Boot.
Create a WebSocket chat with Ktor.
Build web applications with Spring Boot and Kotlin.
Create an interactive website with Ktor.
Use Spring Boot with Kotlin and RSocket.
Publish server-side Kotlin applications: Ktor on Heroku.

2. 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.

3. Learn more about Kotlin for server-side:

How to write your first unit test.

How to mix Kotlin and Java code in your application.

4. Join the Kotlin server-side community:

Slack: get an invite and join the #getting-started, #server, #spring, or #ktor channels.

StackOverflow: subscribe to the "kotlin", "spring-kotlin", or "ktor" tags.

5. Follow Kotlin on Twitter, Reddit, and Youtube, and don't miss any important ecosystem updates.

If you've encountered any difficulties or problems, report an issue to ourissue tracker.

Cross-platform

Here you'll learn how to develop and improve your cross-platform application usingKotlin Multiplatform.

1. Set up your environment for cross-platform development.

2. Create your first application for iOS and Android:

66
To start from scratch, create a basic cross-platform 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 iOStutorial.

If you prefer real-life examples, clone and play with an existing project, for example the networking and data storage project from theCreate a multiplatform app using Ktor and
SQLdelight tutorial or any sample project.

3. Use a wide set of multiplatform libraries to implement the required business logic only once in the shared module. Learn more aboutadding dependencies.

Library Details

Ktor Docs

Serialization Docs and sample

Coroutines Docs and sample

DateTime Docs

SQLDelight Third-party library.


Docs

You can also find a multiplatform library in the community-driven list.

4. Learn more about Kotlin Multiplatform:

Learn more about Kotlin Multiplatform.

Look through samples projects.

Publish a multiplatform library.

Learn how Kotlin Multiplatform is used at Netflix, VMware, Yandex, and many other companies.

5. Join the Kotlin Multiplatform community:

Slack: get an invite and join the #getting-started and #multiplatform channels.

StackOverflow: Subscribe to the "kotlin-multiplatform" tag.

6. Follow Kotlin on Twitter, Reddit, and Youtube, and don't miss any important ecosystem updates.

If you've encountered any difficulties or problems, report an issue to ourissue tracker.

Android

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.

Data
analysis

From building data pipelines to productionizing machine learning models, Kotlin is a great choice for working with data and getting the most out of it.

1. Create and edit notebooks seamlessly within the IDE:

Get started with Kotlin Notebook.

2. Explore and experiment with your data:

DataFrame – a library for data analysis and manipulation.

Kandy – a plotting tool for data visualization.

3. Get the latest updates about Kotlin for Data Analysis:

Slack: get an invite and join the #datascience channel.

67
Twitter: follow KotlinForData.

4. Follow Kotlin on Twitter, Reddit, and Youtube, and don't miss any important ecosystem updates.

Is anything missing?
If anything is missing or seems confusing on this page, please share your feedback.

Welcome to our tour of Kotlin!

This tour covers the fundamentals of the Kotlin programming language and can be completed entirely within your browser. There is no installation
required.

Each chapter in this tour contains:

Theory to introduce key concepts of the language with examples.

Practice with exercises to test your understanding of what you have learned.

Solutions for your reference.

In this tour you will learn about:

Variables

Basic types

Collections

Control flow

Functions

Classes

Null safety

To have the best experience, we recommend that you read through these chapters in order. But if you want, you can choose which chapters you want to read.

Ready to go?

Start the Kotlin tour

Hello world
Here is a simple program that prints "Hello, world!":

fun main() {
println("Hello, world!")
// Hello, world!
}

In Kotlin:

fun is used to declare a function

the main() function is where your program starts from

the body of a function is written within curly braces {}

println() and print() functions print their arguments to standard output

68
Functions are discussed in more detail in a couple of chapters. Until then, all examples use the main() function.

Variables
All programs need to be able to store data, and variables help you to do just that. In Kotlin, you can declare:

read-only variables with val

mutable variables with var

To assign a value, use the assignment operator =.

For example:

fun main() {
//sampleStart
val popcorn = 5 // There are 5 boxes of popcorn
val hotdog = 7 // There are 7 hotdogs
var customers = 10 // There are 10 customers in the queue

// Some customers leave the queue


customers = 8
println(customers)
// 8
//sampleEnd
}

Variables can be declared outside the main() function at the beginning of your program. Variables declared in this way are said to be declared at top level.

As customers is a mutable variable, its value can be reassigned after declaration.

We recommend that you declare all variables as read-only (val) by default. Declare mutable variables (var) only if necessary.

String templates
It's useful to know how to print the contents of variables to standard output. You can do this with string templates. You can use template expressions to access
data stored in variables and other objects, and convert them into strings. A string value is a sequence of characters in double quotes ". Template expressions
always start with a dollar sign $.

To evaluate a piece of code in a template expression, place the code within curly braces {} after the dollar sign $.

For example:

fun main() {
//sampleStart
val customers = 10
println("There are $customers customers")
// There are 10 customers

println("There are ${customers + 1} customers")


// There are 11 customers
//sampleEnd
}

For more information, see String templates.

You will notice that there aren't any types declared for variables. Kotlin has inferred the type itself: Int. This tour explains the different Kotlin basic types and how to
declare them in the next chapter.

Practice

69
Exercise
Complete the code to make the program print "Mary is 20 years old" to standard output:

fun main() {
val name = "Mary"
val age = 20
// Write your code here
}

fun main() { val name = "Mary" val age = 20 println("$name is $age years old") }

Next step
Basic types

Basic types
Every variable and data structure in Kotlin has a data type. Data types are important because they tell the compiler what you are allowed to do with that variable or
data structure. In other words, what functions and properties it has.

In the last chapter, Kotlin was able to tell in the previous example that customers has type: Int. Kotlin's ability to infer the data type is called type inference.
customers is assigned an integer value. From this, Kotlin infers that customers has numerical data type: Int. As a result, the compiler knows that you can perform
arithmetic operations with customers:

fun main() {
//sampleStart
var customers = 10

// Some customers leave the queue


customers = 8

customers = customers + 3 // Example of addition: 11


customers += 7 // Example of addition: 18
customers -= 3 // Example of subtraction: 15
customers *= 2 // Example of multiplication: 30
customers /= 3 // Example of division: 10

println(customers) // 10
//sampleEnd
}

+=, -=, *=, /=, and %= are augmented assignment operators. For more information, see Augmented assignments.

In total, Kotlin has the following basic types:

Category Basic types

Integers Byte, Short, Int, Long

Unsigned integers UByte, UShort, UInt, ULong

Floating-point numbers Float, Double

Booleans Boolean

70
Category Basic types

Characters Char

Strings String

For more information on basic types and their properties, see Basic types.

With this knowledge, you can declare variables and initialize them later. Kotlin can manage this as long as variables are initialized before the first read.

To declare a variable without initializing it, specify its type with :.

For example:

fun main() {
//sampleStart
// Variable declared without initialization
val d: Int
// Variable initialized
d = 3

// Variable explicitly typed and initialized


val e: String = "hello"

// Variables can be read because they have been initialized


println(d) // 3
println(e) // hello
//sampleEnd
}

Now that you know how to declare basic types, it's time to learn about collections.

Practice

Exercise
Explicitly declare the correct type for each variable:

fun main() {
val a = 1000
val b = "log message"
val c = 3.14
val d = 100_000_000_000_000
val e = false
val f = '\n'
}

fun main() { val a: Int = 1000 val b: String = "log message" val c: Double = 3.14 val d: Long = 100_000_000_000 val e: Boolean = false val f:
Char = '\n' }

Next step
Collections

Collections
When programming, it is useful to be able to group data into structures for later processing. Kotlin provides collections for exactly this purpose.

Kotlin has the following collections for grouping items:

71
Collection type Description

Lists Ordered collections of items

Sets Unique unordered collections of items

Maps Sets of key-value pairs where keys are unique and map to only one value

Each collection type can be mutable or read only.

List
Lists store items in the order that they are added, and allow for duplicate items.

To create a read-only list (List), use the listOf() function.

To create a mutable list (MutableList), use the mutableListOf() function.

When creating lists, Kotlin can infer the type of items stored. To declare the type explicitly, add the type within angled brackets <> after the list declaration:

fun main() {
//sampleStart
// Read only list
val readOnlyShapes = listOf("triangle", "square", "circle")
println(readOnlyShapes)
// [triangle, square, circle]

// Mutable list with explicit type declaration


val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle")
println(shapes)
// [triangle, square, circle]
//sampleEnd
}

To prevent unwanted modifications, you can obtain read-only views of mutable lists by assigning them to a List:

val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle")


val shapesLocked: List<String> = shapes

This is also called casting.

Lists are ordered so to access an item in a list, use the indexed access operator []:

fun main() {
//sampleStart
val readOnlyShapes = listOf("triangle", "square", "circle")
println("The first item in the list is: ${readOnlyShapes[0]}")
// The first item in the list is: triangle
//sampleEnd
}

To get the first or last item in a list, use .first() and .last() functions respectively:

fun main() {
//sampleStart
val readOnlyShapes = listOf("triangle", "square", "circle")
println("The first item in the list is: ${readOnlyShapes.first()}")
// The first item in the list is: triangle
//sampleEnd
}

72
.first() and .last() functions are examples of extension functions. To call an extension function on an object, write the function name after the object
appended with a period .

For more information about extension functions, see Extension functions. For the purposes of this tour, you only need to know how to call them.

To get the number of items in a list, use the .count() function:

fun main() {
//sampleStart
val readOnlyShapes = listOf("triangle", "square", "circle")
println("This list has ${readOnlyShapes.count()} items")
// This list has 3 items
//sampleEnd
}

To check that an item is in a list, use the in operator:

fun main() {
//sampleStart
val readOnlyShapes = listOf("triangle", "square", "circle")
println("circle" in readOnlyShapes)
// true
//sampleEnd
}

To add or remove items from a mutable list, use .add() and .remove() functions respectively:

fun main() {
//sampleStart
val shapes: MutableList<String> = mutableListOf("triangle", "square", "circle")
// Add "pentagon" to the list
shapes.add("pentagon")
println(shapes)
// [triangle, square, circle, pentagon]

// Remove the first "pentagon" from the list


shapes.remove("pentagon")
println(shapes)
// [triangle, square, circle]
//sampleEnd
}

Set
Whereas lists are ordered and allow duplicate items, sets are unordered and only store unique items.

To create a read-only set (Set), use the setOf() function.

To create a mutable set (MutableSet), use the mutableSetOf() function.

When creating sets, Kotlin can infer the type of items stored. To declare the type explicitly, add the type within angled brackets <> after the set declaration:

fun main() {
//sampleStart
// Read-only set
val readOnlyFruit = setOf("apple", "banana", "cherry", "cherry")
// Mutable set with explicit type declaration
val fruit: MutableSet<String> = mutableSetOf("apple", "banana", "cherry", "cherry")

println(readOnlyFruit)
// [apple, banana, cherry]
//sampleEnd
}

You can see in the previous example that because sets only contain unique elements, the duplicate "cherry" item is dropped.

73
To prevent unwanted modifications, obtain read-only views of mutable sets by casting them to Set:

val fruit: MutableSet<String> = mutableSetOf("apple", "banana", "cherry", "cherry")


val fruitLocked: Set<String> = fruit

As sets are unordered, you can't access an item at a particular index.

To get the number of items in a set, use the .count() function:

fun main() {
//sampleStart
val readOnlyFruit = setOf("apple", "banana", "cherry", "cherry")
println("This set has ${readOnlyFruit.count()} items")
// This set has 3 items
//sampleEnd
}

To check that an item is in a set, use the in operator:

fun main() {
//sampleStart
val readOnlyFruit = setOf("apple", "banana", "cherry", "cherry")
println("banana" in readOnlyFruit)
// true
//sampleEnd
}

To add or remove items from a mutable set, use .add() and .remove() functions respectively:

fun main() {
//sampleStart
val fruit: MutableSet<String> = mutableSetOf("apple", "banana", "cherry", "cherry")
fruit.add("dragonfruit") // Add "dragonfruit" to the set
println(fruit) // [apple, banana, cherry, dragonfruit]

fruit.remove("dragonfruit") // Remove "dragonfruit" from the set


println(fruit) // [apple, banana, cherry]
//sampleEnd
}

Map
Maps store items as key-value pairs. You access the value by referencing the key. You can imagine a map like a food menu. You can find the price (value), by
finding the food (key) you want to eat. Maps are useful if you want to look up a value without using a numbered index, like in a list.

Every key in a map must be unique so that Kotlin can understand which value you want to get.

You can have duplicate values in a map.

To create a read-only map (Map), use the mapOf() function.

To create a mutable map (MutableMap), use the mutableMapOf() function.

When creating maps, Kotlin can infer the type of items stored. To declare the type explicitly, add the types of the keys and values within angled brackets <> after
the map declaration. For example: MutableMap<String, Int>. The keys have type String and the values have type Int.

The easiest way to create maps is to use to between each key and its related value:

fun main() {

74
//sampleStart
// Read-only map
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(readOnlyJuiceMenu)
// {apple=100, kiwi=190, orange=100}

// Mutable map with explicit type declaration


val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(juiceMenu)
// {apple=100, kiwi=190, orange=100}
//sampleEnd
}

To prevent unwanted modifications, obtain read-only views of mutable maps by casting them to Map:

val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
val juiceMenuLocked: Map<String, Int> = juiceMenu

To access a value in a map, use the indexed access operator [] with its key:

fun main() {
//sampleStart
// Read-only map
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println("The value of apple juice is: ${readOnlyJuiceMenu["apple"]}")
// The value of apple juice is: 100
//sampleEnd
}

To get the number of items in a map, use the .count() function:

fun main() {
//sampleStart
// Read-only map
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println("This map has ${readOnlyJuiceMenu.count()} key-value pairs")
// This map has 3 key-value pairs
//sampleEnd
}

To add or remove items from a mutable map, use .put() and .remove() functions respectively:

fun main() {
//sampleStart
val juiceMenu: MutableMap<String, Int> = mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
juiceMenu.put("coconut", 150) // Add key "coconut" with value 150 to the map
println(juiceMenu)
// {apple=100, kiwi=190, orange=100, coconut=150}

juiceMenu.remove("orange") // Remove key "orange" from the map


println(juiceMenu)
// {apple=100, kiwi=190, coconut=150}
//sampleEnd
}

To check if a specific key is already included in a map, use the .containsKey() function:

fun main() {
//sampleStart
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(readOnlyJuiceMenu.containsKey("kiwi"))
// true
//sampleEnd
}

To obtain a collection of the keys or values of a map, use the keys and values properties respectively:

75
fun main() {
//sampleStart
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(readOnlyJuiceMenu.keys)
// [apple, kiwi, orange]
println(readOnlyJuiceMenu.values)
// [100, 190, 100]
//sampleEnd
}

keys and values are examples of properties of an object. To access the property of an object, write the property name after the object appended with a
period .

Properties are discussed in more detail in the Classes chapter. At this point in the tour, you only need to know how to access them.

To check that a key or value is in a map, use the in operator:

fun main() {
//sampleStart
val readOnlyJuiceMenu = mapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println("orange" in readOnlyJuiceMenu.keys)
// true
println(200 in readOnlyJuiceMenu.values)
// false
//sampleEnd
}

For more information on what you can do with collections, see Collections.

Now that you know about basic types and how to manage collections, it's time to explore the control flow that you can use in your programs.

Practice

Exercise 1
You have a list of “green” numbers and a list of “red” numbers. Complete the code to print how many numbers there are in total.

fun main() {
val greenNumbers = listOf(1, 4, 23)
val redNumbers = listOf(17, 2)
// Write your code here
}

fun main() { val greenNumbers = listOf(1, 4, 23) val redNumbers = listOf(17, 2) val totalCount = greenNumbers.count() + redNumbers.count()
println(totalCount) }

Exercise 2
You have a set of protocols supported by your server. A user requests to use a particular protocol. Complete the program to check whether the requested protocol
is supported or not (isSupported must be a Boolean value).

fun main() {
val SUPPORTED = setOf("HTTP", "HTTPS", "FTP")
val requested = "smtp"
val isSupported = // Write your code here
println("Support for $requested: $isSupported")
}

Hint
Make sure that you check the requested protocol in upper case. You can use the .uppercase() function to help you with this.

fun main() { val SUPPORTED = setOf("HTTP", "HTTPS", "FTP") val requested = "smtp" val isSupported = requested.uppercase() in

76
SUPPORTED println("Support for $requested: $isSupported") }

Exercise 3
Define a map that relates integer numbers from 1 to 3 to their corresponding spelling. Use this map to spell the given number.

fun main() {
val number2word = // Write your code here
val n = 2
println("$n is spelt as '${<Write your code here >}'")
}

fun main() { val number2word = mapOf(1 to "one", 2 to "two", 3 to "three") val n = 2 println("$n is spelt as '${number2word[n]}'") }

Next step
Control flow

Control flow
Like other programming languages, Kotlin is capable of making decisions based on whether a piece of code is evaluated to be true. Such pieces of code are called
conditional expressions. Kotlin is also able to create and iterate through loops.

Conditional expressions
Kotlin provides if and when for checking conditional expressions.

If you have to choose between if and when, we recommend using when as it leads to more robust and safer programs.

If
To use if, add the conditional expression within parentheses () and the action to take if the result is true within curly braces {}:

fun main() {
//sampleStart
val d: Int
val check = true

if (check) {
d = 1
} else {
d = 2
}

println(d)
// 1
//sampleEnd
}

There is no ternary operator condition ? then : else in Kotlin. Instead, if can be used as an expression. If there is only one line of code per action, the curly braces {}
are optional:

fun main() {
//sampleStart
val a = 1
val b = 2

println(if (a > b) a else b) // Returns a value: 2


//sampleEnd
}

77
When
Use when when you have a conditional expression with multiple branches. when can be used either as a statement or as an expression.

Here is an example of using when as a statement:

Place the conditional expression within parentheses () and the actions to take within curly braces {}.

Use -> in each branch to separate each condition from each action.

fun main() {
//sampleStart
val obj = "Hello"

when (obj) {
// Checks whether obj equals to "1"
"1" -> println("One")
// Checks whether obj equals to "Hello"
"Hello" -> println("Greeting")
// Default statement
else -> println("Unknown")
}
// Greeting
//sampleEnd
}

Note that all branch conditions are checked sequentially until one of them is satisfied. So only the first suitable branch is executed.

Here is an example of using when as an expression. The when syntax is assigned immediately to a variable:

fun main() {
//sampleStart
val obj = "Hello"

val result = when (obj) {


// If obj equals "1", sets result to "one"
"1" -> "One"
// If obj equals "Hello", sets result to "Greeting"
"Hello" -> "Greeting"
// Sets result to "Unknown" if no previous condition is satisfied
else -> "Unknown"
}
println(result)
// Greeting
//sampleEnd
}

If when is used as an expression, the else branch is mandatory, unless the compiler can detect that all possible cases are covered by the branch conditions.

The previous example showed that when is useful for matching a variable. when is also useful when you need to check a chain of Boolean expressions:

fun main() {
//sampleStart
val temp = 18

val description = when {


// If temp < 0 is true, sets description to "very cold"
temp < 0 -> "very cold"
// If temp < 10 is true, sets description to "a bit cold"
temp < 10 -> "a bit cold"
// If temp < 20 is true, sets description to "warm"
temp < 20 -> "warm"
// Sets description to "hot" if no previous condition is satisfied
else -> "hot"
}
println(description)
// warm
//sampleEnd
}

78
Ranges
Before talking about loops, it's useful to know how to construct ranges for loops to iterate over.

The most common way to create a range in Kotlin is to use the .. operator. For example, 1..4 is equivalent to 1, 2, 3, 4.

To declare a range that doesn't include the end value, use the ..< operator. For example, 1..<4 is equivalent to 1, 2, 3.

To declare a range in reverse order, use downTo. For example, 4 downTo 1 is equivalent to 4, 3, 2, 1.

To declare a range that increments in a step that isn't 1, use step and your desired increment value. For example, 1..5 step 2 is equivalent to 1, 3, 5.

You can also do the same with Char ranges:

'a'..'d' is equivalent to 'a', 'b', 'c', 'd'

'z' downTo 's' step 2 is equivalent to 'z', 'x', 'v', 't'

Loops
The two most common loop structures in programming are for and while. Use for to iterate over a range of values and perform an action. Use while to continue an
action until a particular condition is satisfied.

For
Using your new knowledge of ranges, you can create a for loop that iterates over numbers 1 to 5 and prints the number each time.

Place the iterator and range within parentheses () with keyword in. Add the action you want to complete within curly braces {}:

fun main() {
//sampleStart
for (number in 1..5) {
// number is the iterator and 1..5 is the range
print(number)
}
// 12345
//sampleEnd
}

Collections can also be iterated over by loops:

fun main() {
//sampleStart
val cakes = listOf("carrot", "cheese", "chocolate")

for (cake in cakes) {


println("Yummy, it's a $cake cake!")
}
// Yummy, it's a carrot cake!
// Yummy, it's a cheese cake!
// Yummy, it's a chocolate cake!
//sampleEnd
}

While
while can be used in two ways:

To execute a code block while a conditional expression is true. (while)

To execute the code block first and then check the conditional expression. (do-while)

In the first use case (while):

Declare the conditional expression for your while loop to continue within parentheses ().

Add the action you want to complete within curly braces {}.

79
The following examples use the increment operator ++ to increment the value of the cakesEaten variable.

fun main() {
//sampleStart
var cakesEaten = 0
while (cakesEaten < 3) {
println("Eat a cake")
cakesEaten++
}
// Eat a cake
// Eat a cake
// Eat a cake
//sampleEnd
}

In the second use case (do-while):

Declare the conditional expression for your while loop to continue within parentheses ().

Define the action you want to complete within curly braces {} with the keyword do.

fun main() {
//sampleStart
var cakesEaten = 0
var cakesBaked = 0
while (cakesEaten < 3) {
println("Eat a cake")
cakesEaten++
}
do {
println("Bake a cake")
cakesBaked++
} while (cakesBaked < cakesEaten)
// Eat a cake
// Eat a cake
// Eat a cake
// Bake a cake
// Bake a cake
// Bake a cake
//sampleEnd
}

For more information and examples of conditional expressions and loops, see Conditions and loops.

Now that you know the fundamentals of Kotlin control flow, it's time to learn how to write your own functions.

Practice

Exercise 1
Using a when expression, update the following program so that when you input the names of GameBoy buttons, the actions are printed to output.

Button Action

A Yes

B No

X Menu

Y Nothing

80
Button Action

Other There is no such button

fun main() {
val button = "A"

println(
// Write your code here
)
}

fun main() { val button = "A" println( when (button) { "A" -> "Yes" "B" -> "No" "X" -> "Menu" "Y" -> "Nothing" else -> "There is no such
button" } ) }

Exercise 2
You have a program that counts pizza slices until there’s a whole pizza with 8 slices. Refactor this program in two ways:

Use a while loop.

Use a do-while loop.

fun main() {
var pizzaSlices = 0
// Start refactoring here
pizzaSlices++
println("There's only $pizzaSlices slice/s of pizza :(")
pizzaSlices++
println("There's only $pizzaSlices slice/s of pizza :(")
pizzaSlices++
println("There's only $pizzaSlices slice/s of pizza :(")
pizzaSlices++
println("There's only $pizzaSlices slice/s of pizza :(")
pizzaSlices++
println("There's only $pizzaSlices slice/s of pizza :(")
pizzaSlices++
println("There's only $pizzaSlices slice/s of pizza :(")
pizzaSlices++
println("There's only $pizzaSlices slice/s of pizza :(")
pizzaSlices++
// End refactoring here
println("There are $pizzaSlices slices of pizza. Hooray! We have a whole pizza! :D")
}

fun main() { var pizzaSlices = 0 while ( pizzaSlices < 7 ) { pizzaSlices++ println("There's only $pizzaSlices slice/s of pizza :(") } pizzaSlices++
println("There are $pizzaSlices slices of pizza. Hooray! We have a whole pizza! :D") }
fun main() { var pizzaSlices = 0 pizzaSlices++ do { println("There's only $pizzaSlices slice/s of pizza :(") pizzaSlices++ } while ( pizzaSlices < 8
) println("There are $pizzaSlices slices of pizza. Hooray! We have a whole pizza! :D") }

Exercise 3
Write a program that simulates the Fizz buzz game. Your task is to print numbers from 1 to 100 incrementally, replacing any number divisible by three with the word
"fizz", and any number divisible by five with the word "buzz". Any number divisible by both 3 and 5 must be replaced with the word "fizzbuzz".

Hint
Use a for loop to count numbers and a when expression to decide what to print at each step.

fun main() {
// Write your code here
}

fun main() { for (number in 1..100) { println( when { number % 15 == 0 -> "fizzbuzz" number % 3 == 0 -> "fizz" number % 5 == 0 -> "buzz"
else -> "$number" } ) } }

81
Exercise 4
You have a list of words. Use for and if to print only the words that start with the letter l.

Hint
Use the .startsWith() function for String type.

fun main() {
val words = listOf("dinosaur", "limousine", "magazine", "language")
// Write your code here
}

fun main() { val words = listOf("dinosaur", "limousine", "magazine", "language") for (w in words) { if (w.startsWith("l")) println(w) } }

Next step
Functions

Functions
You can declare your own functions in Kotlin using the fun keyword.

fun hello() {
return println("Hello, world!")
}

fun main() {
hello()
// Hello, world!
}

In Kotlin:

function parameters are written within parentheses ().

each parameter must have a type, and multiple parameters must be separated by commas ,.

the return type is written after the function's parentheses (), separated by a colon :.

the body of a function is written within curly braces {}.

the return keyword is used to exit or return something from a function.

If a function doesn't return anything useful, the return type and return keyword can be omitted. Learn more about this in Functions without return.

In the following example:

x and y are function parameters.

x and y have type Int.

the function's return type is Int.

the function returns a sum of x and y when called.

fun sum(x: Int, y: Int): Int {


return x + y
}

fun main() {
println(sum(1, 2))
// 3
}

82
We recommend in our coding conventions that you name functions starting with a lowercase letter and use camel case with no underscores.

Named arguments
For concise code, when calling your function, you don't have to include parameter names. However, including parameter names does make your code easier to
read. This is called using named arguments. If you do include parameter names, then you can write the parameters in any order.

In the following example, string templates ($) are used to access the parameter values, convert them to String type, and then concatenate them into a
string for printing.

fun printMessageWithPrefix(message: String, prefix: String) {


println("[$prefix] $message")
}

fun main() {
// Uses named arguments with swapped parameter order
printMessageWithPrefix(prefix = "Log", message = "Hello")
// [Log] Hello
}

Default parameter values


You can define default values for your function parameters. Any parameter with a default value can be omitted when calling your function. To declare a default
value, use the assignment operator = after the type:

fun printMessageWithPrefix(message: String, prefix: String = "Info") {


println("[$prefix] $message")
}

fun main() {
// Function called with both parameters
printMessageWithPrefix("Hello", "Log")
// [Log] Hello

// Function called only with message parameter


printMessageWithPrefix("Hello")
// [Info] Hello

printMessageWithPrefix(prefix = "Log", message = "Hello")


// [Log] Hello
}

You can skip specific parameters with default values, rather than omitting them all. However, after the first skipped parameter, you must name all
subsequent parameters.

Functions without return


If your function doesn't return a useful value then its return type is Unit. Unit is a type with only one value – Unit. You don't have to declare that Unit is returned
explicitly in your function body. This means that you don't have to use the return keyword or declare a return type:

fun printMessage(message: String) {


println(message)
// `return Unit` or `return` is optional
}

fun main() {
printMessage("Hello")
// Hello
}

83
Single-expression functions
To make your code more concise, you can use single-expression functions. For example, the sum() function can be shortened:

fun sum(x: Int, y: Int): Int {


return x + y
}

fun main() {
println(sum(1, 2))
// 3
}

You can remove the curly braces {} and declare the function body using the assignment operator =. And due to Kotlin's type inference, you can also omit the return
type. The sum() function then becomes one line:

fun sum(x: Int, y: Int) = x + y

fun main() {
println(sum(1, 2))
// 3
}

Omitting the return type is only possible when your function has no body ({}). Unless your function's return type is Unit.

Functions practice

Exercise 1
Write a function called circleArea that takes the radius of a circle in integer format as a parameter and outputs the area of that circle.

In this exercise, you import a package so that you can access the value of pi via PI. For more information about importing packages, see Packages and
imports.

import kotlin.math.PI

fun circleArea() {
// Write your code here
}
fun main() {
println(circleArea(2))
}

import kotlin.math.PI fun circleArea(radius: Int): Double { return PI * radius * radius } fun main() { println(circleArea(2)) // 12.566370614359172
}

Exercise 2
Rewrite the circleArea function from the previous exercise as a single-expression function.

import kotlin.math.PI

// Write your code here

fun main() {
println(circleArea(2))
}

import kotlin.math.PI fun circleArea(radius: Int): Double = PI * radius * radius fun main() { println(circleArea(2)) // 12.566370614359172 }

84
Exercise 3
You have a function that translates a time interval given in hours, minutes, and seconds into seconds. In most cases, you need to pass only one or two function
parameters while the rest are equal to 0. Improve the function and the code that calls it by using default parameter values and named arguments so that the code is
easier to read.

fun intervalInSeconds(hours: Int, minutes: Int, seconds: Int) =


((hours * 60) + minutes) * 60 + seconds

fun main() {
println(intervalInSeconds(1, 20, 15))
println(intervalInSeconds(0, 1, 25))
println(intervalInSeconds(2, 0, 0))
println(intervalInSeconds(0, 10, 0))
println(intervalInSeconds(1, 0, 1))
}

fun intervalInSeconds(hours: Int = 0, minutes: Int = 0, seconds: Int = 0) = ((hours * 60) + minutes) * 60 + seconds fun main() {
println(intervalInSeconds(1, 20, 15)) println(intervalInSeconds(minutes = 1, seconds = 25)) println(intervalInSeconds(hours = 2))
println(intervalInSeconds(minutes = 10)) println(intervalInSeconds(hours = 1, seconds = 1)) }

Lambda expressions
Kotlin allows you to write even more concise code for functions by using lambda expressions.

For example, the following uppercaseString() function:

fun uppercaseString(text: String): String {


return text.uppercase()
}
fun main() {
println(uppercaseString("hello"))
// HELLO
}

Can also be written as a lambda expression:

fun main() {
println({ text: String -> text.uppercase() }("hello"))
// HELLO
}

Lambda expressions can be hard to understand at first glance so let's break it down. Lambda expressions are written within curly braces {}.

Within the lambda expression, you write:

the parameters followed by an ->.

the function body after the ->.

In the previous example:

text is a function parameter.

text has type String.

the function returns the result of the .uppercase() function called on text.

If you declare a lambda without parameters, then there is no need to use ->. For example:

{ println("Log message") }

Lambda expressions can be used in a number of ways. You can:

assign a lambda to a variable that you can then invoke later

85
pass a lambda expression as a parameter to another function

return a lambda expression from a function

invoke a lambda expression on its own

Assign to variable
To assign a lambda expression to a variable, use the assignment operator =:

fun main() {
val upperCaseString = { text: String -> text.uppercase() }
println(upperCaseString("hello"))
// HELLO
}

Pass to another function


A great example of when it is useful to pass a lambda expression to a function, is using the .filter() function on collections:

fun main() {
//sampleStart
val numbers = listOf(1, -2, 3, -4, 5, -6)
val positives = numbers.filter { x -> x > 0 }
val negatives = numbers.filter { x -> x < 0 }
println(positives)
// [1, 3, 5]
println(negatives)
// [-2, -4, -6]
//sampleEnd
}

The .filter() function accepts a lambda expression as a predicate:

{ x -> x > 0 } takes each element of the list and returns only those that are positive.

{ x -> x < 0 } takes each element of the list and returns only those that are negative.

If a lambda expression is the only function parameter, you can drop the function parentheses (). This is an example of a trailing lambda, which is
discussed in more detail at the end of this chapter.

Another good example, is using the .map() function to transform items in a collection:

fun main() {
//sampleStart
val numbers = listOf(1, -2, 3, -4, 5, -6)
val doubled = numbers.map { x -> x * 2 }
val tripled = numbers.map { x -> x * 3 }
println(doubled)
// [2, -4, 6, -8, 10, -12]
println(tripled)
// [3, -6, 9, -12, 15, -18]
//sampleEnd
}

The .map() function accepts a lambda expression as a transform function:

{ x -> x * 2 } takes each element of the list and returns that element multiplied by 2.

{ x -> x * 3 } takes each element of the list and returns that element multiplied by 3.

Function types
Before you can return a lambda expression from a function, you first need to understand function types.

You have already learned about basic types but functions themselves also have a type. Kotlin's type inference can infer a function's type from the parameter type.

86
But there may be times when you need to explicitly specify the function type. The compiler needs the function type so that it knows what is and isn't allowed for
that function.

The syntax for a function type has:

each parameter's type written within parentheses () and separated by commas ,.

the return type written after ->.

For example: (String) -> String or (Int, Int) -> Int.

This is what a lambda expression looks like if a function type for upperCaseString() is defined:

val upperCaseString: (String) -> String = { text -> text.uppercase() }

fun main() {
println(upperCaseString("hello"))
// HELLO
}

If your lambda expression has no parameters then the parentheses () are left empty. For example: () -> Unit

You must declare parameter and return types either in the lambda expression or as a function type. Otherwise, the compiler won't be able to know what
type your lambda expression is.

For example, the following won't work:

val upperCaseString = { str -> str.uppercase() }

Return from a function


Lambda expressions can be returned from a function. So that the compiler understands what type the lambda expression returned is, you must declare a function
type.

In the following example, the toSeconds() function has function type (Int) -> Int because it always returns a lambda expression that takes a parameter of type Int and
returns an Int value.

This example uses a when expression to determine which lambda expression is returned when toSeconds() is called:

fun toSeconds(time: String): (Int) -> Int = when (time) {


"hour" -> { value -> value * 60 * 60 }
"minute" -> { value -> value * 60 }
"second" -> { value -> value }
else -> { value -> value }
}

fun main() {
val timesInMinutes = listOf(2, 10, 15, 1)
val min2sec = toSeconds("minute")
val totalTimeInSeconds = timesInMinutes.map(min2sec).sum()
println("Total time is $totalTimeInSeconds secs")
// Total time is 1680 secs
}

Invoke separately
Lambda expressions can be invoked on their own by adding parentheses () after the curly braces {} and including any parameters within the parentheses:

fun main() {
//sampleStart
println({ text: String -> text.uppercase() }("hello"))
// HELLO
//sampleEnd
}

Trailing lambdas

87
As you have already seen, if a lambda expression is the only function parameter, you can drop the function parentheses (). If a lambda expression is passed as the
last parameter of a function, then the expression can be written outside the function parentheses (). In both cases, this syntax is called a trailing lambda.

For example, the .fold() function accepts an initial value and an operation:

fun main() {
//sampleStart
// The initial value is zero.
// The operation sums the initial value with every item in the list cumulatively.
println(listOf(1, 2, 3).fold(0, { x, item -> x + item })) // 6

// Alternatively, in the form of a trailing lambda


println(listOf(1, 2, 3).fold(0) { x, item -> x + item }) // 6
//sampleEnd
}

For more information on lambda expressions, see Lambda expressions and anonymous functions.

The next step in our tour is to learn about classes in Kotlin.

Lambda expressions practice

Exercise 1
You have a list of actions supported by a web service, a common prefix for all requests, and an ID of a particular resource. To request an action title over the
resource with ID: 5, you need to create the following URL: https://example.com/book-info/5/title. Use a lambda expression to create a list of URLs from the list of
actions.

fun main() {
val actions = listOf("title", "year", "author")
val prefix = "https://example.com/book-info"
val id = 5
val urls = // Write your code here
println(urls)
}

fun main() { val actions = listOf("title", "year", "author") val prefix = "https://example.com/book-info" val id = 5 val urls = actions.map { action
-> "$prefix/$id/$action" } println(urls) }

Exercise 2
Write a function that takes an Int value and an action (a function with type () -> Unit) which then repeats the action the given number of times. Then use this function
to print “Hello” 5 times.

fun repeatN(n: Int, action: () -> Unit) {


// Write your code here
}

fun main() {
// Write your code here
}

fun repeatN(n: Int, action: () -> Unit) { for (i in 1..n) { action() } } fun main() { repeatN(5) { println("Hello") } }

Next step
Classes

Classes
Kotlin supports object-oriented programming with classes and objects. Objects are useful for storing data in your program. Classes allow you to declare a set of

88
characteristics for an object. When you create objects from a class, you can save time and effort because you don't have to declare these characteristics every
time.

To declare a class, use the class keyword:

class Customer

Properties
Characteristics of a class's object can be declared in properties. You can declare properties for a class:

Within parentheses () after the class name.

class Contact(val id: Int, var email: String)

Within the class body defined by curly braces {}.

class Contact(val id: Int, var email: String) {


val category: String = ""
}

We recommend that you declare properties as read-only (val) unless they need to be changed after an instance of the class is created.

You can declare properties without val or var within parentheses but these properties are not accessible after an instance has been created.

The content contained within parentheses () is called the class header.

You can use a trailing comma when declaring class properties.

Just like with function parameters, class properties can have default values:

class Contact(val id: Int, var email: String = "[email protected]") {


val category: String = "work"
}

Create instance
To create an object from a class, you declare a class instance using a constructor.

By default, Kotlin automatically creates a constructor with the parameters declared in the class header.

For example:

class Contact(val id: Int, var email: String)

fun main() {
val contact = Contact(1, "[email protected]")
}

In the example:

Contact is a class.

contact is an instance of the Contact class.

id and email are properties.

id and email are used with the default constructor to create contact.

Kotlin classes can have many constructors, including ones that you define yourself. To learn more about how to declare multiple constructors, see Constructors.

89
Access properties
To access a property of an instance, write the name of the property after the instance name appended with a period .:

class Contact(val id: Int, var email: String)

fun main() {
val contact = Contact(1, "[email protected]")

// Prints the value of the property: email


println(contact.email)
// [email protected]

// Updates the value of the property: email


contact.email = "[email protected]"

// Prints the new value of the property: email


println(contact.email)
// [email protected]
}

To concatenate the value of a property as part of a string, you can use string templates ( $). For example:

println("Their email address is: ${contact.email}")

Member functions
In addition to declaring properties as part of an object's characteristics, you can also define an object's behavior with member functions.

In Kotlin, member functions must be declared within the class body. To call a member function on an instance, write the function name after the instance name
appended with a period .. For example:

class Contact(val id: Int, var email: String) {


fun printId() {
println(id)
}
}

fun main() {
val contact = Contact(1, "[email protected]")
// Calls member function printId()
contact.printId()
// 1
}

Data classes
Kotlin has data classes which are particularly useful for storing data. Data classes have the same functionality as classes, but they come automatically with
additional member functions. These member functions allow you to easily print the instance to readable output, compare instances of a class, copy instances, and
more. As these functions are automatically available, you don't have to spend time writing the same boilerplate code for each of your classes.

To declare a data class, use the keyword data:

data class User(val name: String, val id: Int)

The most useful predefined member functions of data classes are:

Function Description

90
Function Description

.toString() Prints a readable string of the class instance and its properties.

.equals() or == Compares instances of a class.

.copy() Creates a class instance by copying another, potentially with some different properties.

See the following sections for examples of how to use each function:

Print as string

Compare instances

Copy instance

Print as string
To print a readable string of a class instance, you can explicitly call the .toString() function, or use print functions (println() and print()) which automatically call
.toString() for you:

data class User(val name: String, val id: Int)

fun main() {
//sampleStart
val user = User("Alex", 1)

// Automatically uses toString() function so that output is easy to read


println(user)
// User(name=Alex, id=1)
//sampleEnd
}

This is particularly useful when debugging or creating logs.

Compare instances
To compare data class instances, use the equality operator ==:

data class User(val name: String, val id: Int)

fun main() {
//sampleStart
val user = User("Alex", 1)
val secondUser = User("Alex", 1)
val thirdUser = User("Max", 2)

// Compares user to second user


println("user == secondUser: ${user == secondUser}")
// user == secondUser: true

// Compares user to third user


println("user == thirdUser: ${user == thirdUser}")
// user == thirdUser: false
//sampleEnd
}

Copy instance
To create an exact copy of a data class instance, call the .copy() function on the instance.

To create a copy of a data class instance and change some properties, call the .copy() function on the instance and add replacement values for properties as
function parameters.

91
For example:

data class User(val name: String, val id: Int)

fun main() {
//sampleStart
val user = User("Alex", 1)
val secondUser = User("Alex", 1)
val thirdUser = User("Max", 2)

// Creates an exact copy of user


println(user.copy())
// User(name=Alex, id=1)

// Creates a copy of user with name: "Max"


println(user.copy("Max"))
// User(name=Max, id=1)

// Creates a copy of user with id: 3


println(user.copy(id = 3))
// User(name=Alex, id=3)
//sampleEnd
}

Creating a copy of an instance is safer than modifying the original instance because any code that relies on the original instance isn't affected by the copy and what
you do with it.

For more information about data classes, see Data classes.

The last chapter of this tour is about Kotlin's null safety.

Practice

Exercise 1
Define a data class Employee with two properties: one for a name, and another for a salary. Make sure that the property for salary is mutable, otherwise you won’t
get a salary boost at the end of the year! The main function demonstrates how you can use this data class.

// Write your code here

fun main() {
val emp = Employee("Mary", 20)
println(emp)
emp.salary += 10
println(emp)
}

data class Employee(val name: String, var salary: Int) fun main() { val emp = Employee("Mary", 20) println(emp) emp.salary += 10 println(emp)
}

Exercise 2
To test your code, you need a generator that can create random employees. Define a class with a fixed list of potential names (inside the class body), and that is
configured by a minimum and maximum salary (inside the class header). Once again, the main function demonstrates how you can use this class.

Hint
Lists have an extension function called .random() that returns a random item within a list.

Hint
Random.nextInt(from = ..., until = ...) gives you a random Int number within specified limits.

import kotlin.random.Random

data class Employee(val name: String, var salary: Int)

// Write your code here

fun main() {
val empGen = RandomEmployeeGenerator(10, 30)

92
println(empGen.generateEmployee())
println(empGen.generateEmployee())
println(empGen.generateEmployee())
empGen.minSalary = 50
empGen.maxSalary = 100
println(empGen.generateEmployee())
}

import kotlin.random.Random data class Employee(val name: String, var salary: Int) class RandomEmployeeGenerator(var minSalary: Int, var
maxSalary: Int) { val names = listOf("John", "Mary", "Ann", "Paul", "Jack", "Elizabeth") fun generateEmployee() = Employee(names.random(),
Random.nextInt(from = minSalary, until = maxSalary)) } fun main() { val empGen = RandomEmployeeGenerator(10, 30)
println(empGen.generateEmployee()) println(empGen.generateEmployee()) println(empGen.generateEmployee()) empGen.minSalary = 50
empGen.maxSalary = 100 println(empGen.generateEmployee()) }

Next step
Null safety

Null safety
In Kotlin, it's possible to have a null value. To help prevent issues with null values in your programs, Kotlin has null safety in place. Null safety detects potential
problems with null values at compile time, rather than at run time.

Null safety is a combination of features that allow you to:

explicitly declare when null values are allowed in your program.

check for null values.

use safe calls to properties or functions that may contain null values.

declare actions to take if null values are detected.

Nullable types
Kotlin supports nullable types which allows the possibility for the declared type to have null values. By default, a type is not allowed to accept null values. Nullable
types are declared by explicitly adding ? after the type declaration.

For example:

fun main() {
// neverNull has String type
var neverNull: String = "This can't be null"

// Throws a compiler error


neverNull = null

// nullable has nullable String type


var nullable: String? = "You can keep a null here"

// This is OK
nullable = null

// By default, null values aren't accepted


var inferredNonNull = "The compiler assumes non-nullable"

// Throws a compiler error


inferredNonNull = null

// notNull doesn't accept null values


fun strLength(notNull: String): Int {
return notNull.length
}

println(strLength(neverNull)) // 18
println(strLength(nullable)) // Throws a compiler error
}

93
length is a property of the String class that contains the number of characters within a string.

Check for null values


You can check for the presence of null values within conditional expressions. In the following example, the describeString() function has an if statement that checks
whether maybeString is not null and if its length is greater than zero:

fun describeString(maybeString: String?): String {


if (maybeString != null && maybeString.length > 0) {
return "String of length ${maybeString.length}"
} else {
return "Empty or null string"
}
}

fun main() {
val nullString: String? = null
println(describeString(nullString))
// Empty or null string
}

Use safe calls


To safely access properties of an object that might contain a null value, use the safe call operator ?.. The safe call operator returns null if either the object or one of
its accessed properties is null. This is useful if you want to avoid the presence of null values triggering errors in your code.

In the following example, the lengthString() function uses a safe call to return either the length of the string or null:

fun lengthString(maybeString: String?): Int? = maybeString?.length

fun main() {
val nullString: String? = null
println(lengthString(nullString))
// null
}

Safe calls can be chained so that if any property of an object contains a null value, then null is returned without an error being thrown. For example:

person.company?.address?.country

The safe call operator can also be used to safely call an extension or member function. In this case, a null check is performed before the function is called. If the
check detects a null value, then the call is skipped and null is returned.

In the following example, nullString is null so the invocation of .uppercase() is skipped and null is returned:

fun main() {
val nullString: String? = null
println(nullString?.uppercase())
// null
}

Use Elvis operator


You can provide a default value to return if a null value is detected by using the Elvis operator ?:.

Write on the left-hand side of the Elvis operator what should be checked for a null value. Write on the right-hand side of the Elvis operator what should be returned if
a null value is detected.

In the following example, nullString is null so the safe call to access the length property returns a null value. As a result, the Elvis operator returns 0:

94
fun main() {
val nullString: String? = null
println(nullString?.length ?: 0)
// 0
}

For more information about null safety in Kotlin, see Null safety.

Practice

Exercise
You have the employeeById function that gives you access to a database of employees of a company. Unfortunately, this function returns a value of the Employee?
type, so the result can be null. Your goal is to write a function that returns the salary of an employee when their id is provided, or 0 if the employee is missing from
the database.

data class Employee (val name: String, var salary: Int)

fun employeeById(id: Int) = when(id) {


1 -> Employee("Mary", 20)
2 -> null
3 -> Employee("John", 21)
4 -> Employee("Ann", 23)
else -> null
}

fun salaryById(id: Int) = // Write your code here

fun main() {
println((1..5).sumOf { id -> salaryById(id) })
}

data class Employee (val name: String, var salary: Int) fun employeeById(id: Int) = when(id) { 1 -> Employee("Mary", 20) 2 -> null 3 ->
Employee("John", 21) 4 -> Employee("Ann", 23) else -> null } fun salaryById(id: Int) = employeeById(id)?.salary ?: 0 fun main() {
println((1..5).sumOf { id -> salaryById(id) }) }

What's next?
Congratulations! Now that you have completed the Kotlin tour, check out our tutorials for popular Kotlin applications:

Create a backend application

Create a cross-platform application for Android and iOS

Kotlin Multiplatform
The Kotlin Multiplatform technology is designed to simplify the development of cross-platform projects. It reduces time spent writing and maintaining the same
code for different platforms while retaining the flexibility and benefits of native programming.

Kotlin Multiplatform

Kotlin Multiplatform use cases

Android and iOS applications


Sharing code between mobile platforms is a major Kotlin Multiplatform use case. With Kotlin Multiplatform, you can build cross-platform mobile applications that
share code between Android and iOS projects to implement networking, data storage and data validation, analytics, computations, and other application logic.

Check out the Get started with Kotlin Multiplatform and Create a multiplatform app using Ktor and SQLDelight tutorials, where you will create applications for

95
Android and iOS that include a module with shared code for both platforms.

Thanks to Compose Multiplatform, a Kotlin-based declarative UI framework developed by JetBrains, you can also share UIs across Android and iOS to create fully
cross-platform apps:

Sharing different levels and UI

Check out the Get started with Compose Multiplatform tutorial to create your own mobile application with UIs shared between both platforms.

Multiplatform libraries
Kotlin Multiplatform is also helpful for library authors. You can create a multiplatform library with common code and its platform-specific implementations for JVM,
web, and native platforms. Once published, a multiplatform library can be used as a dependency in other cross-platform projects.

See the Publish a multiplatform library for more details.

Desktop applications
Compose Multiplatform helps share UIs across desktop platforms like Windows, macOS, and Linux. Many applications, including the JetBrains Toolbox app, have
already adopted this approach.

Try this Compose Multiplatform desktop application template to create your own project with UIs shared among desktop platforms.

Code sharing between platforms


Kotlin Multiplatform allows you to maintain a single codebase of the application logic for different platforms. You also get advantages of native programming,
including great performance and full access to platform SDKs.

Kotlin provides the following code sharing mechanisms:

Share common code among all platforms used in your project.

Share code among some platforms included in your project to reuse much of the code in similar platforms:

Code shared across different platforms

If you need to access platform-specific APIs from the shared code, use the Kotlin mechanism of expected and actual declarations.

Get started
Begin with the Get started with Kotlin Multiplatform if you want to create iOS and Android applications with shared code

Explore sharing code principles and examples if you want to create applications or libraries targeting other platforms

New to Kotlin? Take a look at Getting started with Kotlin.

96
Sample projects
Look through cross-platform application samples to understand how Kotlin Multiplatform works.

Kotlin for server side


Kotlin is a great fit for developing server-side applications. It allows you to write concise and expressive code while maintaining full compatibility with existing Java-
based technology stacks, all with 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, so you can use your familiar technology stack while reaping the benefits of a more
modern language.

Migration: Kotlin supports gradual 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 can guide you through the key features of the language with a series of interactive exercises.

Frameworks for server-side development with Kotlin


Here are some examples of the server-side frameworks for 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.

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.

Quarkus provides first class support for using Kotlin. The framework is open source and maintained by Red Hat. Quarkus was built from the ground up for
Kubernetes and provides a cohesive full-stack framework by leveraging a growing list of hundreds of best-of-breed libraries.

Vert.x, a framework for building reactive Web applications on the JVM, offers dedicated support for Kotlin, including full documentation.

kotlinx.html is a DSL that can be used to build HTML in Web applications. 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 microservices and serverless applications. It comes with a lot of
useful built-in 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, and using NoSQL databases through their Java drivers. For JPA, the kotlin-jpa compiler
plugin adapts Kotlin-compiled classes to the requirements of the framework.

You can find more frameworks at https://kotlin.link/.

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.

97
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.

Products that use Kotlin on the server side


Corda is an open-source distributed ledger platform that is 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.

Watch a webinar "Micronaut for microservices with Kotlin" and explore a detailed guide showing how you can use Kotlin extension functions in the Micronaut
framework.

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.

Over 50% of professional Android developers use Kotlin as their primary language, while only 30% use Java as their main language. 70% of developers whose
primary language is Kotlin say that Kotlin makes them more productive.

Using Kotlin for Android development, you can benefit from:

Less code combined with greater readability. Spend less time writing your code and working to understand the code of others.

Fewer common errors. Apps built with Kotlin are 20% less likely to crash based on Google's internal data.

Kotlin support in Jetpack libraries. Jetpack Compose is Android's recommended modern toolkit for building native UI in Kotlin. KTX extensions add Kotlin
language features, like coroutines, extension functions, lambdas, and named parameters to existing Android libraries.

Support for multiplatform development. Kotlin Multiplatform allows development for not only Android but also iOS, backend, and web applications. Some
Jetpack libraries are already multiplatform. Compose Multiplatform, JetBrains' declarative UI framework based on Kotlin and Jetpack Compose, makes it
possible to share UIs across platforms – iOS, Android, desktop, and web.

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 into Android Studio and is actively used by many companies for developing Android applications.

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.

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. Over 95% of the top thousand
Android apps use Kotlin.

Many startups and Fortune 500 companies have already developed Android applications using Kotlin, see the list on the Google website for Android developers.

To start using Kotlin for:

Android development, read Google's documentation for developing Android apps with Kotlin.

Developing cross-platform mobile applications, see Get started with Kotlin Multiplatform for Android and iOS.

Kotlin Wasm

98
Kotlin Wasm is Alpha. It may be changed at any time. You can use it in scenarios before production. We would appreciate your feedback in YouTrack.

Join the Kotlin/Wasm community.

With Kotlin, you have the power to build applications and reuse mobile and desktop user interfaces (UIs) in your web projects through Compose Multiplatform and
Kotlin/Wasm.

Compose Multiplatform is a declarative framework based on Kotlin and Jetpack Compose that allows you to implement the UI once and share it across all the
platforms you target. Specifically for web platforms, Compose Multiplatform uses Kotlin/Wasm as its compilation target.

Explore our online demo of an application built with Compose Multiplatform and Kotlin/Wasm

Kotlin/Wasm demo

To run applications built with Kotlin/Wasm in a browser, you need a browser version that supports the new garbage collection and exception handling
proposals. To check the browser support status, see the WebAssembly roadmap.

WebAssembly (Wasm) is a binary instruction format for a stack-based virtual machine. This format is platform-independent because it runs on its own virtual
machine. Wasm provides Kotlin and other languages with a compilation target to run on the web.

Kotlin/Wasm compiles your Kotlin code into Wasm format. Using Kotlin/Wasm, you can create applications that run on different environments and devices, which
support Wasm and meet Kotlin's requirements.

Additionally, you can use the most popular Kotlin libraries in Kotlin/Wasm out of the box. Like other Kotlin and Multiplatform projects, you can include dependency
declarations in the build script. For more information, see Adding dependencies on multiplatform libraries.

Would you like to try it yourself?

Get started with Kotlin/Wasm

Kotlin/Wasm performance
Although Kotlin/Wasm is still in Alpha, Compose Multiplatform running on Kotlin/Wasm already shows encouraging performance traits. You can see that its

99
execution speed outperforms JavaScript and is approaching that of the JVM:

Kotlin/Wasm performance

We regularly run benchmarks on Kotlin/Wasm, and these results come from our testing in a recent version of Google Chrome.

Browser API support


The Kotlin/Wasm standard library provides declarations for browser APIs, including the DOM API. With these declarations, you can directly use the Kotlin API to
access and utilize various browser functionalities. For example, in your Kotlin/Wasm applications, you can use manipulation with DOM elements or fetch the API
without defining these declarations from scratch. To learn more, see our Kotlin/Wasm browser example.

The declarations for browser API support are defined using JavaScript interoperability capabilities. You can use the same capabilities to define your own
declarations. In addition, Kotlin/Wasm–JavaScript interoperability allows you to use Kotlin code from JavaScript. For more information, see Use Kotlin code in
JavaScript.

Leave feedback

Kotlin/Wasm feedback

Slack: Get a Slack invite and provide your feedback directly to developers in our #webassembly channel.

Report any issues in YouTrack.

Compose Multiplatform feedback

Slack: provide your feedback in the #compose-web public channel.

Report any issues in GitHub.

Learn more
Learn more about Kotlin/Wasm in this YouTube playlist.

Explore the Kotlin/Wasm examples in our GitHub repository.

100
Kotlin Native
Kotlin/Native is a technology for compiling Kotlin code to native binaries which can run without a virtual machine. Kotlin/Native includes an LLVM-based backend
for the Kotlin compiler and a native implementation of the Kotlin standard library.

Why Kotlin/Native?
Kotlin/Native is primarily designed to allow compilation for platforms on which virtual machines are not desirable or possible, such as embedded devices or iOS. It is
ideal for 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

iOS, tvOS, watchOS

Linux

Windows (MinGW)

Android NDK

To compile Apple targets, macOS, iOS, tvOS, and watchOS, you need Xcode and its command-line tools installed.

See the full list of supported targets.

Interoperability
Kotlin/Native supports two-way interoperability with native programming languages for different operating systems. The compiler creates:

an executable for many platforms

a static library or dynamic library with C headers for C/C++ projects

an Apple framework for Swift and Objective-C projects

Kotlin/Native supports interoperability to use existing libraries directly from Kotlin/Native:

static or dynamic C libraries

C, Swift, and Objective-C frameworks

It is easy to include compiled Kotlin code in 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 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 in the compiler package.

Sharing code between platforms


Kotlin Multiplatform helps share common code across multiple platforms, including Android, iOS, JVM, web, and native. Multiplatform libraries provide the
necessary APIs for common Kotlin code and allow writing shared parts of projects in Kotlin all in one place.

You can use the Get started with Kotlin Multiplatform tutorial to create applications and share business logic between iOS and Android. To share UIs among iOS,
Android, desktop, and web, try Compose Multiplatform, JetBrains' declarative UI framework based on Kotlin and Jetpack Compose.

101
How to get started
New to Kotlin? Take a look at Getting started with Kotlin.

Recommended documentation:

Get started with Kotlin Multiplatform

Interoperability with C

Interoperability with Swift/Objective-C

Recommended tutorials:

Get started with Kotlin/Native

Get started with Kotlin Multiplatform

Mapping primitive data types from C

Kotlin/Native as a dynamic Library

Kotlin/Native as an Apple framework

Kotlin for JavaScript


Kotlin/JS provides the ability to transpile your Kotlin code, the Kotlin standard library, and any compatible dependencies to JavaScript. The current implementation
of Kotlin/JS targets ES5.

The recommended way to use Kotlin/JS is via the kotlin.multiplatform Gradle plugin. It lets you easily set up and control Kotlin projects targeting JavaScript in one
place. 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 Set up a Kotlin/JS project.

Kotlin/JS IR compiler
The Kotlin/JS IR compiler comes with a number of improvements over the old default compiler. For example, it reduces the size of generated executables via dead
code elimination and provides smoother interoperability with the JavaScript ecosystem and its tooling.

The old compiler has been deprecated since the Kotlin 1.8.0 release.

By generating TypeScript declaration files (d.ts) from Kotlin code, the IR compiler makes it easier to create "hybrid" applications that mix TypeScript and Kotlin code
and to leverage code-sharing functionality using Kotlin Multiplatform.

To learn more about the available features in the Kotlin/JS IR compiler and how to try it for your project, visit the Kotlin/JS IR compiler documentation page and the
migration guide.

Kotlin/JS frameworks
Modern web development benefits significantly from frameworks that simplify building web applications. Here are a few 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.

Visit KVision site for documentation, tutorials, and examples.

For updates and discussions about the framework, join the #kvision and #javascript channels in the Kotlin Slack.

102
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.

Visit fritz2 site for documentation, tutorials, and examples.

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 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.

Visit Doodle site for documentation, tutorials, and examples.

For updates and discussions about the framework, join the #doodle and #javascript channels in the Kotlin Slack.

Join the Kotlin/JS community


You can join the #javascript channel in the official Kotlin Slack to chat with the community and the team.

Kotlin for data analysis


Exploring and analyzing data is something you may not do every day, but it's a crucial skill you need as a software developer.

Let's think about software development duties where data analysis is key: analyzing what's actually inside collections when debugging, digging into memory dumps
or databases, or receiving JSON files with large amounts of data when working with REST APIs, to mention some.

With Kotlin's Exploratory Data Analysis (EDA) tools, such as Kotlin notebooks, Kotlin DataFrame, and Kandy, you have at your disposal a rich set of capabilities to
enhance your analytics skills and support you across different scenarios:

Load, transform, and visualize data in various formats: with our Kotlin EDA tools, you can perform tasks like filtering, sorting, and aggregating data. Our tools can
seamlessly read data right in the IDE from different file formats, including CSV, JSON, and TXT.

Kandy, our plotting tool, allows you to create a wide range of charts to visualize and gain insights from the dataset.

Efficiently analyze data stored in relational databases: Kotlin DataFrame seamlessly integrates with databases and provides capabilities similar to SQL queries.
You can retrieve, manipulate, and visualize data directly from various databases.

Fetch and analyze real-time and dynamic datasets from web APIs: the EDA tools' flexibility allows integration with external APIs via protocols like OpenAPI. This
feature helps you fetch data from web APIs, to then clean and transform the data to your needs.

Would you like to try our Kotlin tools for data analysis?

Get started with Kotlin Notebook

Our Kotlin data analysis tools let you smoothly handle your data from start to finish. Effortlessly retrieve your data with simple drag-and-drop functionality in our
Kotlin Notebook. Clean, transform, and visualize it with just a few lines of code. Additionally, export your output charts in a matter of clicks.

103
Kotlin Notebook

Notebooks
Notebooks are interactive editors that integrate code, graphics, and text in a single environment. When using a notebook, you can run code cells and immediately
see the output.

Kotlin offers different notebook solutions, such as Kotlin Notebook, Datalore, and Kotlin-Jupyter Notebook, providing convenient features for data retrieving,
transformation, exploration, modeling, and more. These Kotlin notebook solutions are based on our Kotlin Kernel.

You can seamlessly share your code among Kotlin Notebook, Datalore, and Kotlin-Jupyter Notebook. Create a project in one of our Kotlin notebooks and continue
working in another notebook without compatibility issues.

Benefit from the features of our powerful Kotlin notebooks and the perks of coding with Kotlin. Kotlin integrates with these notebooks to help you manage data and
share your findings with colleagues while building up your data science and machine learning skills.

Discover the features of our different Kotlin notebook solutions and choose the one that best aligns with your project requirements.

104
Kotlin Notebook

Kotlin Notebook
The Kotlin Notebook is a plugin for IntelliJ IDEA that allows you to create notebooks in Kotlin. It provides our IDE experience with all common IDE features, offering
real-time code insights and project integration.

Kotlin notebooks in Datalore


With Datalore, you can use Kotlin in the browser straight out of the box without additional installation. You can also share your notebooks and run them remotely,
collaborate with other Kotlin notebooks in real-time, receive smart coding assistance as you write code, and export results through interactive or static reports.

Jupyter Notebook with Kotlin Kernel


Jupyter Notebook is an open-source web application that allows you to create and share documents containing code, visualizations, and Markdown text. Kotlin-
Jupyter is an open-source project that brings Kotlin support to Jupyter Notebook to harness Kotlin's power within the Jupyter environment.

Kotlin DataFrame

105
The Kotlin DataFrame library lets you manipulate structured data in your Kotlin projects. From data creation and cleaning to in-depth analysis and feature
engineering, this library has you covered.

With the Kotlin DataFrame library, you can work with different file formats, including CSV, JSON, XLS, and XLSX. This library also facilitates the data retrieval
process with its ability to connect with SQL databases or APIs.

Kotlin DataFrame

Kandy
Kandy is an open-source Kotlin library that provides a powerful and flexible DSL for plotting charts of various types. This library is a simple, idiomatic, readable, and
type-safe tool to visualize data.

Kandy has seamless integration with Kotlin Notebook, Datalore, and Kotlin-Jupyter Notebook. You can also easily combine the Kandy and Kotlin DataFrame
libraries to complete different data-related tasks.

106
Kandy

What's next
Get started with Kotlin Notebook.

Retrieve and transform data using the Kotlin DataFrame library.

Visualize data using the Kandy library.

Learn more about Kotlin and Java libraries for data analysis.

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 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

107
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, you 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.
You can implement the function f in functional style, using such Kotlin features as tail recursion:

tailrec fun removeZeroes(x: Int): Int =


if (x % 10 == 0) removeZeroes(x / 10) else x

fun f(x: Int) = removeZeroes(x + 1)

Alternatively, you can write an imperative implementation of the function f using the traditional while loop and mutable variables that are denoted in Kotlin with var:

fun f(x: Int): Int {


var cur = x + 1
while (cur % 10 == 0) cur /= 10
return cur
}

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 collections and data-structures like
dynamically-sized arrays (ArrayList), hash-based maps and sets (HashMap/HashSet), tree-based ordered maps and sets (TreeMap/TreeSet). 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:

Kotlin 1.6.0 and later

fun main() {
var n = readln().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
}

There is no need to handle the case of misformatted input in competitive programming. An input format is always precisely specified in competitive programming, and the actual input
cannot deviate from the input specification in the problem statement. That's why you can use Kotlin's readln() function. 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.

Earlier versions

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.

108
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().

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:

Kotlin 1.6.0 and later

private fun readStr() = readln() // string line


private fun readInt() = readStr().toInt() // single int
// similar for other types you'd use in your solutions

Earlier versions

private fun readStr() = readLine()!! // string line


private fun readInt() = readStr().toInt() // single int
// similar for other types you'd use in your solutions

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:

Kotlin 1.6.0 and later

fun main() {
// read input
val n = readln().toInt()
val s = readln()
val fl = readln().split(" ").map { it.toInt() }
// 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)
}

Earlier versions

fun main() {
// read input
val n = readLine()!!.toInt()
val s = readLine()!!
val fl = readLine()!!.split(" ").map { it.toInt() }
// 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

109
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:

Kotlin 1.6.0 and later

private fun readStr() = readln() // string line


private fun readInt() = readStr().toInt() // single int
private fun readStrings() = readStr().split(" ") // list of strings
private fun readInts() = readStrings().map { it.toInt() } // list of ints

Earlier versions

private fun readStr() = readLine()!! // string line


private fun readInt() = readStr().toInt() // single int
private fun readStrings() = readStr().split(" ") // list of strings
private fun readInts() = readStrings().map { it.toInt() } // list of ints

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 = readStr()
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, and others for indices, r, and c for row and
column numbers in tables, x and y for coordinates, and so on. 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:

The first line of the input contains two integers n and k

In Kotlin this line can be concisely parsed with the following statement using destructuring declaration from a list of integers:

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 105 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 105 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:

println(a.joinToString("\n")) // each element of array/list of a separate line

Learning Kotlin

110
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 website 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 2.0.0


Released: May 21, 2024

The Kotlin 2.0.0 release is out and the new Kotlin K2 compiler is Stable! Additionally, here are some other highlights:

New Compose compiler Gradle plugin

Generation of lambda functions using invokedynamic

The kotlinx-metadata-jvm library is now Stable

Monitoring GC performance in Kotlin/Native with signposts on Apple platforms

Resolving conflicts in Kotlin/Native with Objective-C methods

Support for named export in Kotlin/Wasm

Support for unsigned primitive types in functions with @JsExport in Kotlin/Wasm

Optimize production builds by default using Binaryen

New Gradle DSL for compiler options in multiplatform projects

Stable replacement of the enum class values generic function

Stable AutoCloseable interface

IDE support
The Kotlin plugins that support Kotlin 2.0.0 are bundled in the latest IntelliJ IDEA and Android Studio. You don't need to update the Kotlin plugin in your IDE. All you
need to do is to change the Kotlin version to Kotlin 2.0.0 in your build scripts.

For details about IntelliJ IDEA's support for the Kotlin K2 compiler, see Support in IDEs.

For more details about IntelliJ IDEA's support for Kotlin, see Kotlin releases.

Kotlin K2 compiler
The road to the K2 compiler has been a long one, but now the JetBrains team is finally ready to announce its stabilization. In Kotlin 2.0.0, the new Kotlin K2
compiler is used by default and it is Stable for all target platforms: JVM, Native, Wasm, and JS. The new compiler brings major performance improvements, speeds
up new language feature development, unifies all platforms that Kotlin supports, and provides a better architecture for multiplatform projects.

The JetBrains team has ensured the quality of the new compiler by successfully compiling 10 million lines of code from selected user and internal projects. 18,000
developers were involved in the stabilization process, testing the new K2 compiler across a total of 80,000 projects and reporting any problems they found.

To help make the migration process to the new compiler as smooth as possible, we've created a K2 compiler migration guide. This guide explains the many
benefits of the compiler, highlights any changes you might encounter, and describes how to roll back to the previous version if necessary.

In a blog post, we explored the performance of the K2 compiler in different projects. Check it out if you'd like to see real data on how the K2 compiler performs and
find instructions on how to collect performance benchmarks from your own projects.

Current K2 compiler limitations


Enabling K2 in your Gradle project comes with certain limitations that can affect projects using Gradle versions below 8.3 in the following cases:

111
Compilation of source code from buildSrc.

Compilation of Gradle plugins in included builds.

Compilation of other Gradle plugins if they are used in projects with Gradle versions below 8.3.

Building Gradle plugin dependencies.

If you encounter any of the problems mentioned above, you can take the following steps to address them:

Set the language version for buildSrc, any Gradle plugins, and their dependencies:

kotlin {
compilerOptions {
languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9)
}
}

If you configure language and API versions for specific tasks, these values will override the values set by the compilerOptions extension. In this case,
language and API versions should not be higher than 1.9.

Update the Gradle version in your project to 8.3 or later.

Smart cast improvements


The Kotlin compiler can automatically cast an object to a type in specific cases, saving you the trouble of having to explicitly cast it yourself. This is called smart
casting. The Kotlin K2 compiler now performs smart casts in even more scenarios than before.

In Kotlin 2.0.0, we've made improvements related to smart casts in the following areas:

Local variables and further scopes

Type checks with logical or operator

Inline functions

Properties with function types

Exception handling

Increment and decrement operators

Local variables and further scopes


Previously, if a variable was evaluated as not null within an if condition, the variable would be smart-cast. Information about this variable would then be shared
further within the scope of the if block.

However, if you declared the variable outside the if condition, no information about the variable would be available within the if condition, so it couldn't be smart-
cast. This behavior was also seen with when expressions and while loops.

From Kotlin 2.0.0, if you declare a variable before using it in your if, when, or while condition, then any information collected by the compiler about the variable will
be accessible in the corresponding block for smart-casting.

This can be useful when you want to do things like extract boolean conditions into variables. Then, you can give the variable a meaningful name, which will improve
your code readability and make it possible to reuse the variable later in your code. For example:

class Cat {
fun purr() {
println("Purr purr")
}
}

fun petAnimal(animal: Any) {


val isCat = animal is Cat
if (isCat) {
// In Kotlin 2.0.0, the compiler can access
// information about isCat, so it knows that

112
// animal was smart-cast to the type Cat.
// Therefore, the purr() function can be called.
// In Kotlin 1.9.20, the compiler doesn't know
// about the smart cast, so calling the purr()
// function triggers an error.
animal.purr()
}
}

fun main() {
val kitty = Cat()
petAnimal(kitty)
// Purr purr
}

Type checks with logical or operator


In Kotlin 2.0.0, if you combine type checks for objects with an or operator (||), a smart cast is made to their closest common supertype. Before this change, a smart
cast was always made to the Any type.

In this case, you still had to manually check the object type afterward before you could access any of its properties or call its functions. For example:

interface Status {
fun signal() {}
}

interface Ok : Status
interface Postponed : Status
interface Declined : Status

fun signalCheck(signalStatus: Any) {


if (signalStatus is Postponed || signalStatus is Declined) {
// signalStatus is smart-cast to a common supertype Status
signalStatus.signal()
// Prior to Kotlin 2.0.0, signalStatus is smart cast
// to type Any, so calling the signal() function triggered an
// Unresolved reference error. The signal() function can only
// be called successfully after another type check:

// check(signalStatus is Status)
// signalStatus.signal()
}
}

The common supertype is an approximation of a union type. Union types are not supported in Kotlin.

Inline functions
In Kotlin 2.0.0, the K2 compiler treats inline functions differently, allowing it to determine in combination with other compiler analyses whether it's safe to smart-
cast.

Specifically, inline functions are now treated as having an implicit callsInPlace contract. This means that any lambda functions passed to an inline function are
called in place. Since lambda functions are called in place, the compiler knows that a lambda function can't leak references to any variables contained within its
function body.

The compiler uses this knowledge along with other compiler analyses to decide whether it's safe to smart-cast any of the captured variables. For example:

interface Processor {
fun process()
}

inline fun inlineAction(f: () -> Unit) = f()

fun nextProcessor(): Processor? = null

fun runProcessor(): Processor? {


var processor: Processor? = null
inlineAction {
// In Kotlin 2.0.0, the compiler knows that processor
// is a local variable, and inlineAction() is an inline function, so
// references to processor can't be leaked. Therefore, it's safe
// to smart-cast processor.

113
// If processor isn't null, processor is smart-cast
if (processor != null) {
// The compiler knows that processor isn't null, so no safe call
// is needed
processor.process()

// In Kotlin 1.9.20, you have to perform a safe call:


// processor?.process()
}

processor = nextProcessor()
}

return processor
}

Properties with function types


In previous versions of Kotlin, there was a bug that meant that class properties with a function type weren't smart-cast. We fixed this behavior in Kotlin 2.0.0 and
the K2 compiler. For example:

class Holder(val provider: (() -> Unit)?) {


fun process() {
// In Kotlin 2.0.0, if provider isn't null, then
// provider is smart-cast
if (provider != null) {
// The compiler knows that provider isn't null
provider()

// In 1.9.20, the compiler doesn't know that provider isn't


// null, so it triggers an error:
// Reference has a nullable type '(() -> Unit)?', use explicit '?.invoke()' to make a function-like call instead
}
}
}

This change also applies if you overload your invoke operator. For example:

interface Provider {
operator fun invoke()
}

interface Processor : () -> String

class Holder(val provider: Provider?, val processor: Processor?) {


fun process() {
if (provider != null) {
provider()
// In 1.9.20, the compiler triggers an error:
// Reference has a nullable type 'Provider?' use explicit '?.invoke()' to make a function-like call instead
}
}
}

Exception handling
In Kotlin 2.0.0, we've made improvements to exception handling so that smart cast information can be passed on to catch and finally blocks. This change makes
your code safer as the compiler keeps track of whether your object has a nullable type. For example:

//sampleStart
fun testString() {
var stringInput: String? = null
// stringInput is smart-cast to String type
stringInput = ""
try {
// The compiler knows that stringInput isn't null
println(stringInput.length)
// 0

// The compiler rejects previous smart cast information for


// stringInput. Now stringInput has the String? type.
stringInput = null

// Trigger an exception
if (2 > 1) throw Exception()

114
stringInput = ""
} catch (exception: Exception) {
// In Kotlin 2.0.0, the compiler knows stringInput
// can be null, so stringInput stays nullable.
println(stringInput?.length)
// null

// In Kotlin 1.9.20, the compiler says that a safe call isn't


// needed, but this is incorrect.
}
}

//sampleEnd
fun main() {
testString()
}

Increment and decrement operators


Prior to Kotlin 2.0.0, the compiler didn't understand that the type of an object can change after using an increment or decrement operator. As the compiler couldn't
accurately track the object type, your code could lead to unresolved reference errors. In Kotlin 2.0.0, this has been fixed:

interface Rho {
operator fun inc(): Sigma = TODO()
}

interface Sigma : Rho {


fun sigma() = Unit
}

interface Tau {
fun tau() = Unit
}

fun main(input: Rho) {


var unknownObject: Rho = input

// Check if unknownObject inherits from the Tau interface


// Note, it's possible that unknownObject inherits from both
// Rho and Tau interfaces.
if (unknownObject is Tau) {

// Uses the overloaded inc() operator from interface Rho,


// which smart casts the type of unknownObject to Sigma.
++unknownObject

// In Kotlin 2.0.0, the compiler knows unknownObject has type


// Sigma, so the sigma() function can be called successfully.
unknownObject.sigma()

// In Kotlin 1.9.20, the compiler thinks unknownObject has type


// Tau, so calling the sigma() function is not allowed.

// In Kotlin 2.0.0, the compiler knows unknownObject has type


// Sigma, so calling the tau() function is not allowed.
unknownObject.tau()
// Unresolved reference 'tau'

// In Kotlin 1.9.20, the compiler mistakenly thinks that


// unknownObject has type Tau, the tau() function can be
// called successfully.
}
}

Kotlin Multiplatform improvements


In Kotlin 2.0.0, we've made improvements in the K2 compiler related to Kotlin Multiplatform in the following areas:

Separation of common and platform sources during compilation

Different visibility levels of expected and actual declarations

Separation of common and platform sources during compilation


Previously, the design of the Kotlin compiler prevented it from keeping common and platform source sets separate at compile time. As a consequence, common

115
code could access platform code, which resulted in different behavior between platforms. In addition, some compiler settings and dependencies from common
code used to leak into platform code.

In Kotlin 2.0.0, our implementation of the new Kotlin K2 compiler included a redesign of the compilation scheme to ensure strict separation between common and
platform source sets. This change is most noticeable when you use expected and actual functions. Previously, it was possible for a function call in your common
code to resolve to a function in platform code. For example:

Common code Platform code

fun foo(x: Any) = println("common foo") // JVM


fun foo(x: Int) = println("platform foo")
fun exampleFunction() {
foo(42) // JavaScript
} // There is no foo() function overload
// on the JavaScript platform

In this example, the common code has different behavior depending on which platform it is run on:

On the JVM platform, calling the foo() function in the common code results in the foo() function from the platform code being called as platform foo.

On the JavaScript platform, calling the foo() function in the common code results in the foo() function from the common code being called as common foo, as
there is no such function available in the platform code.

In Kotlin 2.0.0, common code doesn't have access to platform code, so both platforms successfully resolve the foo() function to the foo() function in the common
code: common foo.

In addition to the improved consistency of behavior across platforms, we also worked hard to fix cases where there was conflicting behavior between IntelliJ IDEA
or Android Studio and the compiler. For instance, when you used expected and actual classes, the following would happen:

Common code Platform code

expect class Identity { actual class Identity {


fun confirmIdentity(): String actual fun confirmIdentity() = "expect class fun:
} jvm"
}
fun common() {
// Before 2.0.0,
// it triggers an IDE-only error
Identity().confirmIdentity()
// RESOLUTION_TO_CLASSIFIER : Expected class
// Identity has no default constructor.
}

In this example, the expected class Identity has no default constructor, so it can't be called successfully in common code. Previously, an error was only reported by
the IDE, but the code still compiled successfully on the JVM. However, now the compiler correctly reports an error:

Expected class 'expect class Identity : Any' does not have default constructor

When resolution behavior doesn't change


We're still in the process of migrating to the new compilation scheme, so the resolution behavior is still the same when you call functions that aren't within the same
source set. You'll notice this difference mainly when you use overloads from a multiplatform library in your common code.

Suppose you have a library, which has two whichFun() functions with different signatures:

// Example library

// MODULE: common
fun whichFun(x: Any) = println("common function")

// MODULE: JVM
fun whichFun(x: Int) = println("platform function")

116
If you call the whichFun() function in your common code, the function that has the most relevant argument type in the library is resolved:

// A project that uses the example library for the JVM target

// MODULE: common
fun main() {
whichFun(2)
// platform function
}

In comparison, if you declare the overloads for whichFun() within the same source set, the function from the common code will be resolved because your code
doesn't have access to the platform-specific version:

// Example library isn't used

// MODULE: common
fun whichFun(x: Any) = println("common function")

fun main() {
whichFun(2)
// common function
}

// MODULE: JVM
fun whichFun(x: Int) = println("platform function")

Similar to multiplatform libraries, since the commonTest module is in a separate source set, it also still has access to platform-specific code. Therefore, the
resolution of calls to functions in the commonTest module exhibits the same behavior as in the old compilation scheme.

In the future, these remaining cases will be more consistent with the new compilation scheme.

Different visibility levels of expected and actual declarations


Before Kotlin 2.0.0, if you used expected and actual declarations in your Kotlin Multiplatform project, they had to have the same visibility level. Kotlin 2.0.0 now also
supports different visibility levels but only if the actual declaration is more permissive than the expected declaration. For example:

expect internal class Attribute // Visibility is internal


actual class Attribute // Visibility is public by default,
// which is more permissive

Similarly, if you are using a type alias in your actual declaration, the visibility of the underlying type should be the same or more permissive than the expected
declaration. For example:

expect internal class Attribute // Visibility is internal


internal actual typealias Attribute = Expanded

class Expanded // Visibility is public by default,


// which is more permissive

Compiler plugins support


Currently, the Kotlin K2 compiler supports the following Kotlin compiler plugins:

all-open

AtomicFU

jvm-abi-gen

js-plain-objects

kapt

Lombok

no-arg

Parcelize

117
SAM with receiver

serialization

Power-assert

In addition, the Kotlin K2 compiler supports:

The Jetpack Compose compiler plugin 2.0.0, which was moved into the Kotlin repository.

The Kotlin Symbol Processing (KSP) plugin since KSP2.

If you use any additional compiler plugins, check their documentation to see if they are compatible with K2.

Experimental Kotlin Power-assert compiler plugin

The Kotlin Power-assert plugin is Experimental. It may be changed at any time.

Kotlin 2.0.0 introduces an experimental Power-assert compiler plugin. This plugin improves the experience of writing tests by including contextual information in
failure messages, making debugging easier and more efficient.

Developers often need to use complex assertion libraries to write effective tests. The Power-assert plugin simplifies this process by automatically generating failure
messages that include intermediate values of the assertion expression. This helps developers quickly understand why a test failed.

When an assertion fails in a test, the improved error message shows the values of all variables and sub-expressions within the assertion, making it clear which part
of the condition caused the failure. This is particularly useful for complex assertions where multiple conditions are checked.

To enable the plugin in your project, configure it in your build.gradle(.kts) file:

Kotlin

plugins {
kotlin("multiplatform") version "2.0.0"
kotlin("plugin.power-assert") version "2.0.0"
}

powerAssert {
functions = listOf("kotlin.assert", "kotlin.test.assertTrue")
}

Groovy

plugins {
id 'org.jetbrains.kotlin.multiplatform' version '2.0.0'
id 'org.jetbrains.kotlin.plugin.power-assert' version '2.0.0'
}

powerAssert {
functions = ["kotlin.assert", "kotlin.test.assertTrue"]
}

Learn more about the Kotlin Power-assert plugin in the documentation.

How to enable the Kotlin K2 compiler


Starting with Kotlin 2.0.0, the Kotlin K2 compiler is enabled by default. No additional actions are required.

Try the Kotlin K2 compiler in Kotlin Playground


Kotlin Playground supports the 2.0.0 release. Check it out!

118
Support in IDEs
By default, IntelliJ IDEA and Android Studio still use the previous compiler for code analysis, code completion, highlighting, and other IDE-related features. To get
the full Kotlin 2.0 experience in your IDE, enable K2 Kotlin mode.

In your IDE, go to Settings | Languages & Frameworks | Kotlin and select the Enable the K2-based Kotlin plugin option. The IDE will analyze your code using its K2
Kotlin mode.

The K2 Kotlin mode is in Alpha and is available starting from 2024.1. The performance and stability of code highlighting and code completion have been
significantly improved, but not all IDE features are supported yet.

After enabling K2 mode, you may notice differences in IDE analysis due to changes in compiler behavior. Learn how the new K2 compiler differs from the previous
one in our migration guide.

Learn more about the K2 Kotlin mode in our blog.

We are actively collecting feedback about K2 Kotlin mode, so please share your thoughts in our public Slack channel.

Leave your feedback on the new K2 compiler


We would appreciate any feedback you may have!

Report any problems you face with the new K2 compiler in our issue tracker.

Enable the "Send usage statistics" option to allow JetBrains to collect anonymous data about K2 usage.

Kotlin/JVM
Starting with version 2.0.0, the compiler can generate classes containing Java 22 bytecode. This version also brings the following changes:

Generation of lambda functions using invokedynamic

The kotlinx-metadata-jvm library is now Stable

Generation of lambda functions using invokedynamic


Kotlin 2.0.0 introduces a new default method for generating lambda functions using invokedynamic. This change reduces the binary sizes of applications compared
to the traditional anonymous class generation.

Since the first version, Kotlin has generated lambdas as anonymous classes. However, starting from Kotlin 1.5.0, the option for invokedynamic generation has been
available by using the -Xlambdas=indy compiler option. In Kotlin 2.0.0, invokedynamic has become the default method for lambda generation. This method
produces lighter binaries and aligns Kotlin with JVM optimizations, ensuring applications benefit from ongoing and future improvements in JVM performance.

Currently, it has three limitations compared to ordinary lambda compilation:

A lambda compiled into invokedynamic is not serializable.

Experimental reflect() API does not support lambdas generated by invokedynamic.

Calling .toString() on such a lambda produces a less readable string representation:

fun main() {
println({})

// With Kotlin 1.9.24 and reflection, returns


// () -> kotlin.Unit

// With Kotlin 2.0.0, returns


// FileKt$$Lambda$13/0x00007f88a0004608@506e1b77
}

To retain the legacy behavior of generating lambda functions, you can either:

Annotate specific lambdas with @JvmSerializableLambda.

Use the compiler option -Xlambdas=class to generate all lambdas in a module using the legacy method.

119
The kotlinx-metadata-jvm library is Stable
In Kotlin 2.0.0, the kotlinx-metadata-jvm library became Stable. Now that the library has changed to the kotlin package and coordinates, you can find it as kotlin-
metadata-jvm (without the "x").

Previously, the kotlinx-metadata-jvm library had its own publishing scheme and version. Now, we will build and publish the kotlin-metadata-jvm updates as part of
the Kotlin release cycle, with the same backward compatibility guarantees as the Kotlin standard library.

The kotlin-metadata-jvm library provides an API to read and modify metadata of binary files generated by the Kotlin/JVM compiler.

Kotlin/Native
This version brings the following changes:

Monitoring GC performance with signposts

Resolving conflicts with Objective-C methods

Changed log level for compiler arguments in Kotlin/Native

Explicitly added standard library and platform dependencies to Kotlin/Native

Tasks error in Gradle configuration cache

Monitoring GC performance with signposts on Apple platforms


Previously, it was only possible to monitor the performance of Kotlin/Native's garbage collector (GC) by looking into logs. However, these logs were not integrated
with Xcode Instruments, a popular toolkit for investigating issues with iOS apps' performance.

Since Kotlin 2.0.0, GC reports pauses with signposts that are available in Instruments. Signposts allow for custom logging within your app, so now, when debugging
iOS app performance, you can check if a GC pause corresponds to the application freeze.

Learn more about GC performance analysis in the documentation.

Resolving conflicts with Objective-C methods


Objective-C methods can have different names, but the same number and types of parameters. For example, locationManager:didEnterRegion: and
locationManager:didExitRegion:. In Kotlin, these methods have the same signature, so an attempt to use them triggers a conflicting overloads error.

Previously, you had to manually suppress conflicting overloads to avoid this compilation error. To improve Kotlin interoperability with Objective-C, the Kotlin 2.0.0
introduces the new @ObjCSignatureOverride annotation.

The annotation instructs the Kotlin compiler to ignore conflicting overloads, in case several functions with the same argument types but different argument names
are inherited from the Objective-C class.

Applying this annotation is also safer than general error suppression. This annotation can only be used in the case of overriding Objective-C methods, which are
supported and tested, while general suppression may hide important errors and lead to silently broken code.

Changed log level for compiler arguments


In this release, the log level for compiler arguments in Kotlin/Native Gradle tasks, such as compile, link, and cinterop, has changed from info to debug.

With debug as its default value, the log level is consistent with other Gradle compilation tasks and provides detailed debugging information, including all compiler
arguments.

Explicitly added standard library and platform dependencies to Kotlin/Native


Previously, the Kotlin/Native compiler resolved standard library and platform dependencies implicitly, which caused inconsistencies in the way the Kotlin Gradle
plugin worked across Kotlin targets.

Now, each Kotlin/Native Gradle compilation explicitly includes standard library and platform dependencies in its compile-time library path via the
compileDependencyFiles compilation parameter.

Tasks error in Gradle configuration cache


Since Kotlin 2.0.0, you may encounter a configuration cache error with messages indicating: invocation of Task.project at execution time is unsupported.

120
This error appears in tasks such as NativeDistributionCommonizerTask and KotlinNativeCompile.

However, this is a false-positive error. The underlying issue is the presence of tasks that are not compatible with the Gradle configuration cache, like the publish*
task.

This discrepancy may not be immediately apparent, as the error message suggests a different root cause.

As the precise cause isn't explicitly stated in the error report, the Gradle team is already addressing the issue to fix reports.

Kotlin/Wasm
Kotlin 2.0.0 improves performance and interoperability with JavaScript:

Optimized production builds by default using Binaryen

Support for named export

Support for unsigned primitive types in functions with @JsExport

Generation of TypeScript declaration files in Kotlin/Wasm

Support for catching JavaScript exceptions

New exception handling proposal is now supported as an option

The withWasm() function is split into JS and WASI variants

Optimized production builds by default using Binaryen


The Kotlin/Wasm toolchain now applies the Binaryen tool during production compilation to all projects, as opposed to the previous manual setup approach. By our
estimations, it should improve runtime performance and reduce the binary size for your project.

This change only affects production compilation. The development compilation process stays the same.

Support for named export


Previously, all exported declarations from Kotlin/Wasm were imported into JavaScript using default export:

//JavaScript:
import Module from "./index.mjs"

Module.add()

Now, you can import each Kotlin declaration marked with @JsExport by name:

// Kotlin:
@JsExport
fun add(a: Int, b: Int) = a + b

//JavaScript:
import { add } from "./index.mjs"

Named exports make it easier to share code between Kotlin and JavaScript modules. They improve readability and help you manage dependencies between
modules.

Support for unsigned primitive types in functions with @JsExport


Starting from Kotlin 2.0.0, you can use unsigned primitive types inside external declarations and functions with the @JsExport annotation that makes Kotlin/Wasm
functions available in JavaScript code.

This helps to mitigate the previous limitation that prevented the unsigned primitives from being used directly inside exported and external declarations. Now you can
export functions with unsigned primitives as a return or parameter type and consume external declarations that return or consume unsigned primitives.

121
For more information on Kotlin/Wasm interoperability with JavaScript, see the documentation.

Generation of TypeScript declaration files in Kotlin/Wasm

Generating TypeScript declaration files in Kotlin/Wasm is Experimental. It may be dropped or changed at any time.

In Kotlin 2.0.0, the Kotlin/Wasm compiler is now capable of generating TypeScript definitions from any @JsExport declarations in your Kotlin code. These definitions
can be used by IDEs and JavaScript tools to provide code autocompletion, help with type checks, and make it easier to include Kotlin code in JavaScript.

The Kotlin/Wasm compiler collects any top-level functions marked with @JsExport and automatically generates TypeScript definitions in a .d.ts file.

To generate TypeScript definitions, in your build.gradle(.kts) file in the wasmJs {} block, add the generateTypeScriptDefinitions() function:

kotlin {
wasmJs {
binaries.executable()
browser {
}
generateTypeScriptDefinitions()
}
}

Support for catching JavaScript exceptions


Previously, Kotlin/Wasm code could not catch JavaScript exceptions, making it difficult to handle errors originating from the JavaScript side of the program.

In Kotlin 2.0.0, we have implemented support for catching JavaScript exceptions within Kotlin/Wasm. This implementation allows you to use try-catch blocks, with
specific types like Throwable or JsException, to handle these errors properly.

Additionally, finally blocks, which help execute code regardless of exceptions, also work correctly. While we're introducing support for catching JavaScript
exceptions, no additional information is provided when a JavaScript exception, like a call stack, occurs. However, we are working on these implementations.

New exception handling proposal is now supported as an option


In this release, we introduce support for the new version of WebAssembly's exception handling proposal within Kotlin/Wasm.

This update ensures the new proposal aligns with Kotlin requirements, enabling the use of Kotlin/Wasm on virtual machines that only support the latest version of
the proposal.

Activate the new exception handling proposal by using the -Xwasm-use-new-exception-proposal compiler option, which is turned off by default.

The withWasm() function is split into JS and WASI variants


The withWasm() function, which used to provide Wasm targets for hierarchy templates, is deprecated in favor of specialized withWasmJs() and withWasmWasi()
functions.

Now you can separate the WASI and JS targets between different groups in the tree definition.

Kotlin/JS
Among other changes, this version brings modern JS compilation to Kotlin, supporting more features from the ES2015 standard:

New compilation target

Suspend functions as ES2015 generators

Passing arguments to the main function

Per-file compilation for Kotlin/JS projects

Improved collection interoperability

Support for createInstance()

122
Support for type-safe plain JavaScript objects

Support for npm package manager

Changes to compilation tasks

Discontinuing legacy Kotlin/JS JAR artifacts

New compilation target


In Kotlin 2.0.0, we're adding a new compilation target to Kotlin/JS, es2015. This is a new way for you to enable all the ES2015 features supported in Kotlin at once.

You can set it up in your build.gradle(.kts) file like this:

kotlin {
js {
compilerOptions {
target.set("es2015")
}
}
}

The new target automatically turns on ES classes and modules and the newly supported ES generators.

Suspend functions as ES2015 generators


This release introduces Experimental support for ES2015 generators for compiling suspend functions.

Using generators instead of state machines should improve the final bundle size of your project. For example, the JetBrains team managed to decrease the bundle
size of its Space project by 20% by using the ES2015 generators.

Learn more about ES2015 (ECMAScript 2015, ES6) in the official documentation.

Passing arguments to the main function


Starting with Kotlin 2.0.0, you can specify a source of your args for the main() function. This feature makes it easier to work with the command line and pass the
arguments.

To do this, define the js {} block with the new passAsArgumentToMainFunction() function, which returns an array of strings:

kotlin {
js {
binary.executable()
passAsArgumentToMainFunction("Deno.args")
}
}

The function is executed at runtime. It takes the JavaScript expression and uses it as the args: Array<String> argument instead of the main() function call.

Also, if you use the Node.js runtime, you can take advantage of a special alias. It allows you to pass process.argv to the args parameter once instead of adding it
manually every time:

kotlin {
js {
binary.executable()
nodejs {
passProcessArgvToMainFunction()
}
}
}

Per-file compilation for Kotlin/JS projects


Kotlin 2.0.0 introduces a new granularity option for the Kotlin/JS project output. You can now set up a per-file compilation that generates one JavaScript file for
each Kotlin file. It helps to significantly optimize the size of the final bundle and improve the loading time of the program.

Previously, there were only two output options. The Kotlin/JS compiler could generate a single .js file for the whole project. However, this file might be too large and

123
inconvenient to use. Whenever you wanted to use a function from your project, you had to include the entire JavaScript file as a dependency. Alternatively, you
could configure a compilation of a separate .js file for each project module. This is still the default option.

Since module files could also be too large, with Kotlin 2.0.0, we add a more granular output that generates one (or two, if the file contains exported declarations)
JavaScript file per each Kotlin file. To enable the per-file compilation mode:

1. Add the useEsModules() function to your build file to support ECMAScript modules:

// build.gradle.kts
kotlin {
js(IR) {
useEsModules() // Enables ES2015 modules
browser()
}
}

You can also use the new es2015 compilation target for that.

2. Apply the -Xir-per-file compiler option or update your gradle.properties file with:

# gradle.properties
kotlin.js.ir.output.granularity=per-file // `per-module` is the default

Improved collection interoperability


Starting with Kotlin 2.0.0, it's possible to export declarations with a Kotlin collection type inside the signature to JavaScript (and TypeScript). This applies to Set,
Map, and List collection types and their mutable counterparts.

To use Kotlin collections in JavaScript, first mark the necessary declarations with @JsExport annotation:

// Kotlin
@JsExport
data class User(
val name: String,
val friends: List<User> = emptyList()
)

@JsExport
val me = User(
name = "Me",
friends = listOf(User(name = "Kodee"))
)

You can then consume them from JavaScript as regular JavaScript arrays:

// JavaScript
import { User, me, KtList } from "my-module"

const allMyFriendNames = me.friends


.asJsReadonlyArrayView()
.map(x => x.name) // [‘Kodee']

Unfortunately, creating Kotlin collections from JavaScript is still unavailable. We're planning to add this functionality in Kotlin 2.0.20.

Support for createInstance()


Starting with Kotlin 2.0.0, you can use the createInstance() function from the Kotlin/JS target. Previously, it was only available on the JVM.

This function from the KClass interface creates a new instance of the specified class, which is useful for getting the runtime reference to a Kotlin class.

Support for type-safe plain JavaScript objects

The js-plain-objects plugin is Experimental. It may be dropped or changed at any time. The js-plain-objects plugin only supports the K2 compiler.

124
To make it easier to work with JavaScript APIs, in Kotlin 2.0.0, we provide a new plugin: js-plain-objects, which you can use to create type-safe plain JavaScript
objects. The plugin checks your code for any external interfaces that have a @JsPlainObject annotation and adds:

An inline invoke operator function inside the companion object that you can use as a constructor.

A .copy() function that you can use to create a copy of your object while adjusting some of its properties.

For example:

import kotlinx.js.JsPlainObject

@JsPlainObject
external interface User {
var name: String
val age: Int
val email: String?
}

fun main() {
// Creates a JavaScript object
val user = User(name = "Name", age = 10)
// Copies the object and adds an email
val copy = user.copy(age = 11, email = "[email protected]")

println(JSON.stringify(user))
// { "name": "Name", "age": 10 }
println(JSON.stringify(copy))
// { "name": "Name", "age": 11, "email": "[email protected]" }
}

Any JavaScript objects created with this approach are safer because instead of only seeing errors at runtime, you can see them at compile time or even highlighted
by your IDE.

Consider this example, which uses a fetch() function to interact with a JavaScript API using external interfaces to describe the shape of the JavaScript objects:

import kotlinx.js.JsPlainObject

@JsPlainObject
external interface FetchOptions {
val body: String?
val method: String
}

// A wrapper for Window.fetch


suspend fun fetch(url: String, options: FetchOptions? = null) = TODO("Add your custom behavior here")

// A compile-time error is triggered as "metod" is not recognized


// as method
fetch("https://google.com", options = FetchOptions(metod = "POST"))
// A compile-time error is triggered as method is required
fetch("https://google.com", options = FetchOptions(body = "SOME STRING"))

In comparison, if you use the js() function instead to create your JavaScript objects, errors are only found at runtime or aren't triggered at all:

suspend fun fetch(url: String, options: FetchOptions? = null) = TODO("Add your custom behavior here")

// No error is triggered. As "metod" is not recognized, the wrong method


// (GET) is used.
fetch("https://google.com", options = js("{ metod: 'POST' }"))

// By default, the GET method is used. A runtime error is triggered as


// body shouldn't be present.
fetch("https://google.com", options = js("{ body: 'SOME STRING' }"))
// TypeError: Window.fetch: HEAD or GET Request cannot have a body

To use the js-plain-objects plugin, add the following to your build.gradle(.kts) file:

Kotlin

plugins {
kotlin("plugin.js-plain-objects") version "2.0.0"
}

125
Groovy

plugins {
id "org.jetbrains.kotlin.plugin.js-plain-objects" version "2.0.0"
}

Support for npm package manager


Previously, it was only possible for the Kotlin Multiplatform Gradle plugin to use Yarn as a package manager to download and install npm dependencies. From
Kotlin 2.0.0, you can use npm as your package manager instead. Using npm as a package manager means that you have one less tool to manage during your
setup.

For backward compatibility, Yarn is still the default package manager. To use npm as your package manager, set the following property in your gradle.properties
file:

kotlin.js.yarn = false

Changes to compilation tasks


Previously, the webpack and distributeResources compilation tasks both targeted the same directories. Moreover, the distribution task declared the dist as its
output directory as well. This resulted in overlapping outputs and produced a compilation warning.

So, starting with Kotlin 2.0.0, we've implemented the following changes:

The webpack task now targets a separate folder.

The distributeResources task has been completely removed.

The distribution task now has the Copy type and targets the dist folder.

Discontinuing legacy Kotlin/JS JAR artifacts


Starting with Kotlin 2.0.0, the Kotlin distribution no longer contains legacy Kotlin/JS artifacts with the .jar extension. Legacy artifacts were used in the unsupported
old Kotlin/JS compiler and unnecessary for the IR compiler, which uses the klib format.

Gradle improvements
Kotlin 2.0.0 is fully compatible with Gradle 6.8.3 through 8.5. You can also use Gradle versions up to the latest Gradle release, but if you do, keep in mind that you
might encounter deprecation warnings or some new Gradle features might not work.

This version brings the following changes:

New Gradle DSL for compiler options in multiplatform projects

New Compose compiler Gradle plugin

Bumping minimum supported versions

New attribute to distinguish JVM and Android published libraries

Improved Gradle dependency handling for CInteropProcess in Kotlin/Native

Visibility changes in Gradle

New directory for Kotlin data in Gradle projects

Kotlin/Native compiler downloaded when needed

Deprecating old ways of defining compiler options

Bumped minimum AGP supported version

New Gradle property for trying the latest language version

New JSON output format for build reports

126
kapt configurations inherit annotation processors from superconfigurations

Kotlin Gradle plugin no longer uses deprecated Gradle conventions

New Gradle DSL for compiler options in multiplatform projects

This 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.

Prior to Kotlin 2.0.0, configuring compiler options in a multiplatform project with Gradle was only possible at a low level, such as per task, compilation, or source
set. To make it easier to configure compiler options more generally in your projects, Kotlin 2.0.0 comes with a new Gradle DSL.

With this new DSL, you can configure compiler options at the extension level for all the targets and shared source sets like commonMain and at a target level for a
specific target:

kotlin {
compilerOptions {
// Extension-level common compiler options that are used as defaults
// for all targets and shared source sets
allWarningsAsErrors.set(true)
}
jvm {
compilerOptions {
// Target-level JVM compiler options that are used as defaults
// for all compilations in this target
noJdk.set(true)
}
}
}

The overall project configuration now has three layers. The highest is the extension level, then the target level and the lowest is the compilation unit (which is usually
a compilation task):

Kotlin compiler options levels

The settings at a higher level are used as a convention (default) for a lower level:

The values of extension compiler options are the default for target compiler options, including shared source sets, like commonMain, nativeMain, and
commonTest.

The values of target compiler options are used as the default for compilation unit (task) compiler options, for example, compileKotlinJvm and
compileTestKotlinJvm tasks.

127
In turn, configurations made at a lower level override related settings at a higher level:

Task-level compiler options override related configurations at the target or the extension level.

Target-level compiler options override related configurations at the extension level.

When configuring your project, keep in mind that some old ways of setting up compiler options have been deprecated.

We encourage you to try the new DSL out in your multiplatform projects and leave feedback in YouTrack, as we plan to make this DSL the recommended approach
for configuring compiler options.

New Compose compiler Gradle plugin


The Jetpack Compose compiler, which translates composables into Kotlin code, has now been merged into the Kotlin repository. This will help transition Compose
projects to Kotlin 2.0.0, as the Compose compiler will always ship simultaneously with Kotlin. This also bumps the Compose compiler version to 2.0.0.

To use the new Compose compiler in your projects, apply the org.jetbrains.kotlin.plugin.compose Gradle plugin in your build.gradle(.kts) file and set its version
equal to Kotlin 2.0.0.

To learn more about this change and see the migration instructions, see the Compose compiler documentation.

New attribute to distinguish JVM and Android-published libraries


Starting with Kotlin 2.0.0, the org.gradle.jvm.environment Gradle attribute is published by default with all Kotlin variants.

The attribute helps distinguish JVM and Android variants of Kotlin Multiplatform libraries. It indicates that a certain library variant is better suited for a certain JVM
environment. The target environment could be "android", "standard-jvm", or "no-jvm".

Publishing this attribute should make consuming Kotlin Multiplatform libraries with JVM and Android targets more robust from non-multiplatform clients as well,
such as Java-only projects.

If necessary, you can disable attribute publication. To do that, add the following Gradle option to your gradle.properties file:

kotlin.publishJvmEnvironmentAttribute=false

Improved Gradle dependency handling for CInteropProcess in Kotlin/Native


In this release, we enhanced the handling of the defFile property to ensure better Gradle task dependency management in Kotlin/Native projects.

Before this update, Gradle builds could fail if the defFile property was designated as an output of another task that hadn't been executed yet. The workaround for
this issue was to add a dependency on this task:

kotlin {
macosArm64("native") {
compilations.getByName("main") {
cinterops {
val cinterop by creating {
defFileProperty.set(createDefFileTask.flatMap { it.defFile.asFile })
project.tasks.named(interopProcessingTaskName).configure {
dependsOn(createDefFileTask)
}
}
}
}
}
}

To fix this, there is a new RegularFileProperty property called definitionFile. Now, Gradle lazily verifies the presence of the definitionFile property after the connected
task has run later in the build process. This new approach eliminates the need for additional dependencies.

The CInteropProcess task and the CInteropSettings class use the definitionFile property instead of defFile and defFileProperty:

Kotlin

kotlin {
macosArm64("native") {
compilations.getByName("main") {

128
cinterops {
val cinterop by creating {
definitionFile.set(project.file("def-file.def"))
}
}
}
}
}

Groovy

kotlin {
macosArm64("native") {
compilations.main {
cinterops {
cinterop {
definitionFile.set(project.file("def-file.def"))
}
}
}
}
}

defFile and defFileProperty parameters are deprecated.

Visibility changes in Gradle

This change impacts only Kotlin DSL users.

In Kotlin 2.0.0, we've modified the Kotlin Gradle Plugin for better control and safety in your build scripts. Previously, certain Kotlin DSL functions and properties
intended for a specific DSL context would inadvertently leak into other DSL contexts. This leakage could lead to the use of incorrect compiler options, settings
being applied multiple times, and other misconfigurations:

kotlin {
// Target DSL couldn't access methods and properties defined in the
// kotlin{} extension DSL
jvm {
// Compilation DSL couldn't access methods and properties defined
// in the kotlin{} extension DSL and Kotlin jvm{} target DSL
compilations.configureEach {
// Compilation task DSLs couldn't access methods and
// properties defined in the kotlin{} extension, Kotlin jvm{}
// target or Kotlin compilation DSL
compileTaskProvider.configure {
// For example:
explicitApi()
// ERROR as it is defined in the kotlin{} extension DSL
mavenPublication {}
// ERROR as it is defined in the Kotlin jvm{} target DSL
defaultSourceSet {}
// ERROR as it is defined in the Kotlin compilation DSL
}
}
}
}

To fix this issue, we've added the @KotlinGradlePluginDsl annotation, preventing the exposure of the Kotlin Gradle plugin DSL functions and properties to levels
where they are not intended to be available. The following levels are separated from each other:

Kotlin extension

Kotlin target

Kotlin compilation

Kotlin compilation task

For the most popular cases, we've added compiler warnings with suggestions on how to fix them if your build script is configured incorrectly. For example:

129
kotlin {
jvm {
sourceSets.getByName("jvmMain").dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3")
}
}
}

In this case, the warning message for sourceSets is:

[DEPRECATION] 'sourceSets: NamedDomainObjectContainer<KotlinSourceSet>' is deprecated.Accessing 'sourceSets' container on the Kotlin


target level DSL is deprecated. Consider configuring 'sourceSets' on the Kotlin extension level.

We would appreciate your feedback on this change! Share your comments directly to Kotlin developers in our #gradle Slack channel. Get a Slack invite.

New directory for Kotlin data in Gradle projects

With this change, you may need to add the .kotlin directory to your project's .gitignore file.

In Kotlin 1.8.20, the Kotlin Gradle plugin switched to storing its data in the Gradle project cache directory: <project-root-directory>/.gradle/kotlin. However, the
.gradle directory is reserved for Gradle only, and as a result it's not future-proof.

To solve this, as of Kotlin 2.0.0, we will store Kotlin data in your <project-root-directory>/.kotlin by default. We will continue to store some data in the .gradle/kotlin
directory for backward compatibility.

The new Gradle properties you can configure are:

Gradle property Description

kotlin.project.persistent.dir Configures the location where your project-level data is stored. Default: <project-root-directory>/.kotlin

kotlin.project.persistent.dir.gradle.disableWrite A boolean value that controls whether writing Kotlin data to the .gradle directory is disabled. Default: false

Add these properties to the gradle.properties file in your projects for them to take effect.

Kotlin/Native compiler downloaded when needed


Before Kotlin 2.0.0, if you had a Kotlin/Native target configured in the Gradle build script of your multiplatform project, Gradle would always download the
Kotlin/Native compiler in the configuration phase.

This happened even if there was no task to compile code for a Kotlin/Native target that was due to run in the execution phase. Downloading the Kotlin/Native
compiler in this way was particularly inefficient for users who only wanted to check the JVM or JavaScript code in their projects. For example, to perform tests or
checks with their Kotlin project as part of a CI process.

In Kotlin 2.0.0, we changed this behavior in the Kotlin Gradle plugin so that the Kotlin/Native compiler is downloaded in the execution phase and only when a
compilation is requested for a Kotlin/Native target.

In turn, the Kotlin/Native compiler's dependencies are now downloaded not as a part of the compiler, but in the execution phase as well.

If you encounter any issues with the new behavior, you can temporarily switch back to the previous behavior by adding the following Gradle property to your
gradle.properties file:

kotlin.native.toolchain.enabled=false

Starting with Kotlin 1.9.20-Beta, the Kotlin/Native distribution is published to Maven Central along with the CDN.

This allowed us to change how Kotlin looks for and downloads the necessary artifacts. Instead of the CDN, by default, it now uses the Maven repositories that you

130
specified in the repositories {} block of your project.

You can temporarily switch this behavior back by setting the following Gradle property in your gradle.properties file:

kotlin.native.distribution.downloadFromMaven=false.

Please report any problems to our issue tracker YouTrack. Both of these Gradle properties that change the default behavior are temporary and will be removed in
future releases.

Deprecated old ways of defining compiler options


In this release, we continue to refine how you can set up compiler options. It should resolve ambiguity between different ways and make the project configuration
more straightforward.

Since Kotlin 2.0.0, the following DSLs for specifying compiler options are deprecated:

The kotlinOptions DSL from the KotlinCompile interface that implements all Kotlin compilation tasks. Use KotlinCompilationTask<CompilerOptions> instead.

The compilerOptions property with the HasCompilerOptions type from the KotlinCompiation interface. This DSL was inconsistent with other DSLs and configured
the same KotlinCommonCompilerOptions object as compilerOptions inside the KotlinCompilation.compileTaskProvider compilation task, which was confusing.

Instead, we recommend using the compilerOptions property from the Kotlin compilation task:

kotlinCompilation.compileTaskProvider.configure {
compilerOptions { ... }
}

For example:

kotlin {
js(IR) {
compilations.all {
compileTaskProvider.configure {
compilerOptions.freeCompilerArgs.add("-Xerror-tolerance-policy=SYNTAX")
}
}
}
}

The kotlinOptions DSL from the KotlinCompilation interface.

The kotlinOptions DSL from the KotlinNativeArtifactConfig interface, the KotlinNativeLink class, and the KotlinNativeLinkArtifactTask class. Use the toolOptions
DSL instead.

The dceOptions DSL from the KotlinJsDce interface. Use the toolOptions DSL instead.

For more information on how to specify compiler options in the Kotlin Gradle plugin, see How to define options.

Bumped minimum supported AGP version


Starting with Kotlin 2.0.0, the minimum supported Android Gradle plugin version is 7.1.3.

New Gradle property for trying the latest language version


Prior to Kotlin 2.0.0, we had the following Gradle property to try out the new K2 compiler: kotlin.experimental.tryK2. Now that the K2 compiler is enabled by default
in Kotlin 2.0.0, we decided to evolve this property into a new form that you can use to try the latest language version in your projects: kotlin.experimental.tryNext.
When you use this property in your gradle.properties file, the Kotlin Gradle plugin increments the language version to one above the default value for your Kotlin
version. For example, in Kotlin 2.0.0, the default language version is 2.0, so the property configures language version 2.1.

This new Gradle property produces similar metrics in build reports as before with kotlin.experimental.tryK2. The language version configured is included in the
output. For example:

##### 'kotlin.experimental.tryNext' results #####


:app:compileKotlin: 2.1 language version
:lib:compileKotlin: 2.1 language version
##### 100% (2/2) tasks have been compiled with Kotlin 2.1 #####

131
To learn more about how to enable build reports and their content, see Build reports.

New JSON output format for build reports


In Kotlin 1.7.0, we introduced build reports to help track compiler performance. Over time, we've added more metrics to make these reports even more detailed and
helpful when investigating performance issues. Previously, the only output format for a local file was the *.txt format. In Kotlin 2.0.0, we support the JSON output
format to make it even easier to analyze using other tools.

To configure JSON output format for your build reports, declare the following properties in your gradle.properties file:

kotlin.build.report.output=json

// The directory to store your build reports


kotlin.build.report.json.directory="my/directory/path"

Alternatively, you can run the following command:

./gradlew assemble -Pkotlin.build.report.output=json -Pkotlin.build.report.json.directory="my/directory/path"

Once configured, Gradle generates your build reports in the directory that you specify with the name: ${project_name}-date-time-<sequence_number>.json.

Here's an example snippet from a build report with JSON output format that contains build metrics and aggregated metrics:

"buildOperationRecord": [
{
"path": ":lib:compileKotlin",
"classFqName": "org.jetbrains.kotlin.gradle.tasks.KotlinCompile_Decorated",
"startTimeMs": 1714730820601,
"totalTimeMs": 2724,
"buildMetrics": {
"buildTimes": {
"buildTimesNs": {
"CLEAR_OUTPUT": 713417,
"SHRINK_AND_SAVE_CURRENT_CLASSPATH_SNAPSHOT_AFTER_COMPILATION": 19699333,
"IR_TRANSLATION": 281000000,
"NON_INCREMENTAL_LOAD_CURRENT_CLASSPATH_SNAPSHOT": 14088042,
"CALCULATE_OUTPUT_SIZE": 1301500,
"GRADLE_TASK": 2724000000,
"COMPILER_INITIALIZATION": 263000000,
"IR_GENERATION": 74000000,

}
}

"aggregatedMetrics": {
"buildTimes": {
"buildTimesNs": {
"CLEAR_OUTPUT": 782667,
"SHRINK_AND_SAVE_CURRENT_CLASSPATH_SNAPSHOT_AFTER_COMPILATION": 22031833,
"IR_TRANSLATION": 333000000,
"NON_INCREMENTAL_LOAD_CURRENT_CLASSPATH_SNAPSHOT": 14890292,
"CALCULATE_OUTPUT_SIZE": 2370750,
"GRADLE_TASK": 3234000000,
"COMPILER_INITIALIZATION": 292000000,
"IR_GENERATION": 89000000,

}
}

kapt configurations inherit annotation processors from superconfigurations


Prior to Kotlin 2.0.0, if you wanted to define a common set of annotation processors in a separate Gradle configuration and extend this configuration in kapt-specific
configurations for your subprojects, kapt would skip annotation processing because it couldn't find any annotation processors. In Kotlin 2.0.0, kapt can successfully
detect that there are indirect dependencies on your annotation processors.

As an example, for a subproject using Dagger, in your build.gradle(.kts) file, use the following configuration:

val commonAnnotationProcessors by configurations.creating


configurations.named("kapt") { extendsFrom(commonAnnotationProcessors) }

dependencies {

132
implementation("com.google.dagger:dagger:2.48.1")
commonAnnotationProcessors("com.google.dagger:dagger-compiler:2.48.1")
}

In this example, the commonAnnotationProcessors Gradle configuration is your common configuration for annotation processing that you want to be used for all
your projects. You use the extendsFrom() method to add "commonAnnotationProcessors" as a superconfiguration. kapt sees that the
commonAnnotationProcessors Gradle configuration has a dependency on the Dagger annotation processor. Therefore, kapt includes the Dagger annotation
processor in its configuration for annotation processing.

Thanks to Christoph Loy for the implementation!

Kotlin Gradle plugin no longer uses deprecated Gradle conventions


Prior to Kotlin 2.0.0, if you used Gradle 8.2 or higher, the Kotlin Gradle plugin incorrectly used Gradle conventions that had been deprecated in Gradle 8.2. This led
to Gradle reporting build deprecations. In Kotlin 2.0.0, the Kotlin Gradle plugin has been updated to no longer trigger these deprecation warnings when you use
Gradle 8.2 or higher.

Standard library
This release brings further stability to the Kotlin standard library and makes even more existing functions common for all platforms:

Stable replacement of the enum class values generic function

Stable AutoCloseable interface

Common protected property AbstractMutableList.modCount

Common protected function AbstractMutableList.removeRange

Common String.toCharArray(destination)

Stable replacement of the enum class values generic function


In Kotlin 2.0.0, the enumEntries<T>() function becomes Stable. The enumEntries<T>() function is a replacement for the generic enumValues<T>() function. The new
function returns a list of all enum entries for the given enum type T. The entries property for enum classes was previously introduced and also stabilized to replace
the synthetic values() function. For more information about the entries property, see What's new in Kotlin 1.8.20.

The enumValues<T>() function is still supported, but we recommend that you use the enumEntries<T>() function instead because it has less of a
performance impact. Every time you call enumValues<T>(), a new array is created, whereas whenever you call enumEntries<T>(), the same list is returned
each time, which is far more efficient.

For example:

enum class RGB { RED, GREEN, BLUE }

inline fun <reified T : Enum<T>> printAllValues() {


print(enumEntries<T>().joinToString { it.name })
}

printAllValues<RGB>()
// RED, GREEN, BLUE

Stable AutoCloseable interface


In Kotlin 2.0.0, the common AutoCloseable interface becomes Stable. It allows you to easily close resources and includes a couple of useful functions:

The use() extension function, which executes a given block function on the selected resource and then closes it down correctly, whether an exception is thrown
or not.

The AutoCloseable() constructor function, which creates instances of the AutoCloseable interface.

In the example below, we define the XMLWriter interface and assume that there is a resource that implements it. For example, this resource could be a class that
opens a file, writes XML content, and then closes it:

133
interface XMLWriter {
fun document(encoding: String, version: String, content: XMLWriter.() -> Unit)
fun element(name: String, content: XMLWriter.() -> Unit)
fun attribute(name: String, value: String)
fun text(value: String)

fun flushAndClose()
}

fun writeBooksTo(writer: XMLWriter) {


val autoCloseable = AutoCloseable { writer.flushAndClose() }
autoCloseable.use {
writer.document(encoding = "UTF-8", version = "1.0") {
element("bookstore") {
element("book") {
attribute("category", "fiction")
element("title") { text("Harry Potter and the Prisoner of Azkaban") }
element("author") { text("J. K. Rowling") }
element("year") { text("1999") }
element("price") { text("29.99") }
}
element("book") {
attribute("category", "programming")
element("title") { text("Kotlin in Action") }
element("author") { text("Dmitry Jemerov") }
element("author") { text("Svetlana Isakova") }
element("year") { text("2017") }
element("price") { text("25.19") }
}
}
}
}
}

Common protected property AbstractMutableList.modCount


In this release, the modCount protectedproperty of the AbstractMutableList interface becomes common. Previously, the modCount property was available on each
platform but not for the common target. Now, you can create custom implementations of AbstractMutableList and access the property in common code.

The property keeps track of the number of structural modifications made to the collection. This includes operations that change the collection size or alter the list in
a way that may cause iterations in progress to return incorrect results.

You can use the modCount property to register and detect concurrent modifications when implementing a custom list.

Common protected function AbstractMutableList.removeRange


In this release, the removeRange() protectedfunction of the AbstractMutableList interface becomes common. Previously, it was available on each platform but not
for the common target. Now, you can create custom implementations of AbstractMutableList and override the function in common code.

The function removes elements from this list following the specified range. By overriding this function, you can take advantage of the custom implementations and
improve the performance of the list operation.

Common String.toCharArray(destination) function


This release introduces a common String.toCharArray(destination) function. Previously, it was only available on the JVM.

Let's compare it with the existing String.toCharArray() function. It creates a new CharArray that contains characters from the specified string. The new common
String.toCharArray(destination) function, however, moves String characters into an existing destination CharArray. This is useful if you already have a buffer that you
want to fill:

fun main() {
val myString = "Kotlin is awesome!"
val destinationArray = CharArray(myString.length)

// Convert the string and store it in the destinationArray:


myString.toCharArray(destinationArray)

for (char in destinationArray) {


print("$char ")
// K o t l i n i s a w e s o m e !
}
}

134
Install Kotlin 2.0.0
Starting from IntelliJ IDEA 2023.3 and Android Studio Iguana (2023.2.1) Canary 15, the Kotlin plugin is distributed as a bundled plugin included in your IDE. This
means that you can't install the plugin from JetBrains Marketplace anymore.

To update to the new Kotlin version, change the Kotlin version to 2.0.0 in your build scripts.

What's new in Kotlin 2.0.0-RC3


Released: May 10, 2024

This document doesn't cover all of the features of the Early Access Preview (EAP) release, but it highlights some major improvements.

See the full list of changes in the GitHub changelog.

The Kotlin 2.0.0-RC3 release is out! It mostly covers the stabilization of the new Kotlin K2 compiler, which reached its Beta status for all targets since 1.9.20. In
addition, there are new features in Kotlin/Wasm and Kotlin/JS, as well as improvements for the Gradle build tool.

IDE support
The Kotlin plugins that support 2.0.0-RC3 are bundled in the latest IntelliJ IDEA and Android Studio. You don't need to update the Kotlin plugin in your IDE. All you
need to do is to change the Kotlin version to 2.0.0-RC3 in your build scripts.

For details about IntelliJ IDEA's support for the Kotlin K2 compiler, see Support in IntelliJ IDEA.

Kotlin K2 compiler
The JetBrains team is still working on the stabilization of the new Kotlin K2 compiler. The new Kotlin K2 compiler will bring major performance improvements, speed
up new language feature development, unify all platforms that Kotlin supports, and provide a better architecture for multiplatform projects.

The K2 compiler is in Beta for all target platforms: JVM, Native, Wasm, and JS. The JetBrains team has ensured the quality of the new compiler by successfully
compiling dozens of user and internal projects. A large number of users are also involved in the stabilization process, trying the new K2 compiler in their projects
and reporting any problems they find.

Current K2 compiler limitations


Enabling K2 in your Gradle project comes with certain limitations that can affect projects using Gradle versions below 8.3 in the following cases:

Compilation of source code from buildSrc.

Compilation of Gradle plugins in included builds.

Compilation of other Gradle plugins if they are used in projects with Gradle versions below 8.3.

Building Gradle plugin dependencies.

If you encounter any of the problems mentioned above, you can take the following steps to address them:

Set the language version for buildSrc, any Gradle plugins, and their dependencies:

kotlin {
compilerOptions {
languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9)
}
}

135
If you configure language and API versions for specific tasks, these values will override the values set by the compilerOptions extension. In this case,
language and API versions should not be higher than 1.9.

Update the Gradle version in your project to 8.3 or later.

Smart cast improvements


The Kotlin compiler can automatically cast an object to a type in specific cases, saving you the trouble of having to explicitly specify it yourself. This is called smart
casting. The Kotlin K2 compiler now performs smart casts in even more scenarios than before.

In Kotlin 2.0.0-RC3, we've made improvements related to smart casts in the following areas:

Local variables and further scopes

Type checks with logical or operator

Inline functions

Properties with function types

Exception handling

Increment and decrement operators

Local variables and further scopes


Previously, if a variable was evaluated as not null within an if condition, the variable was smart cast, and information about this variable was shared further within
the scope of the if block. However, if you declared the variable outside the if condition, no information about the variable was available within the if condition, so it
couldn't be smart cast. This behavior was also seen with when expressions and while loops.

From Kotlin 2.0.0-RC3, if you declare a variable before using it in your if, when, or while condition then any information collected by the compiler about the variable
is accessible in the condition statement and its block for smart casting. This can be useful when you want to do things like extract boolean conditions into variables.
Then, you can give the variable a meaningful name, which makes your code easier to read, and easily reuse the variable later in your code. For example:

class Cat {
fun purr() {
println("Purr purr")
}
}

fun petAnimal(animal: Any) {


val isCat = animal is Cat
if (isCat) {
// In Kotlin 2.0.0-RC3, the compiler can access
// information about isCat, so it knows that
// animal was smart cast to type Cat.
// Therefore, the purr() function is successfully called.
// In Kotlin 1.9.20, the compiler doesn't know
// about the smart cast, so calling the purr()
// function triggers an error.
animal.purr()
}
}

fun main(){
val kitty = Cat()
petAnimal(kitty)
// Purr purr
}

Type checks with logical or operator


In Kotlin 2.0.0-RC3, if you combine type checks for objects with an or operator (||), a smart cast is made to their closest common supertype. Before this change, a
smart cast was always made to the Any type. In this case, you still had to manually check the object type afterward before you could access any of its properties or
call its functions. For example:

interface Status {
fun signal() {}
}

136
interface Ok : Status
interface Postponed : Status
interface Declined : Status

fun signalCheck(signalStatus: Any) {


if (signalStatus is Postponed || signalStatus is Declined) {
// signalStatus is smart cast to a common supertype Status
signalStatus.signal()
// Prior to Kotlin 2.0.0-RC3, signalStatus is smart cast
// to type Any, so calling the signal() function triggered an
// Unresolved reference error. The signal() function can only
// be called successfully after another type check:

// check(signalStatus is Status)
// signalStatus.signal()
}
}

The common supertype is an approximation of a union type. Union types are not supported in Kotlin.

Inline functions
In Kotlin 2.0.0-RC3, the K2 compiler treats inline functions differently, allowing it to determine in combination with other compiler analyses whether it's safe to smart
cast.

Specifically, inline functions are now treated as having an implicit callsInPlace contract. So, any lambda functions passed to an inline function are called "in place".
Since lambda functions are called in place, the compiler knows that a lambda function can't leak references to any variables contained within its function body. The
compiler uses this knowledge along with other compiler analyses to decide if it's safe to smart cast any of the captured variables. For example:

interface Processor {
fun process()
}

inline fun inlineAction(f: () -> Unit) = f()

fun nextProcessor(): Processor? = null

fun runProcessor(): Processor? {


var processor: Processor? = null
inlineAction {
// In Kotlin 2.0.0-RC3, the compiler knows that processor
// is a local variable, and inlineAction() is an inline function, so
// references to processor can't be leaked. Therefore, it's safe
// to smart cast processor.

// If processor isn't null, processor is smart cast


if (processor != null) {
// The compiler knows that processor isn't null, so no safe call
// is needed
processor.process()

// In Kotlin 1.9.20, you have to perform a safe call:


// processor?.process()
}

processor = nextProcessor()
}

return processor
}

Properties with function types


In previous versions of Kotlin, it was a bug that class properties with a function type weren't smart cast. We fixed this behavior in the K2 compiler in Kotlin 2.0.0-
RC3. For example:

class Holder(val provider: (() -> Unit)?) {


fun process() {
// In Kotlin 2.0.0-RC3, if provider isn't null,
// it is smart cast
if (provider != null) {
// The compiler knows that provider isn't null

137
provider()

// In 1.9.20, the compiler doesn't know that provider isn't


// null, so it triggers an error:
// Reference has a nullable type '(() -> Unit)?', use explicit '?.invoke()' to make a function-like call instead
}
}
}

This change also applies if you overload your invoke operator. For example:

interface Provider {
operator fun invoke()
}

interface Processor : () -> String

class Holder(val provider: Provider?, val processor: Processor?) {


fun process() {
if (provider != null) {
provider()
// In 1.9.20, the compiler triggers an error:
// Reference has a nullable type 'Provider?', use explicit '?.invoke()' to make a function-like call instead
}
}
}

Exception handling
In Kotlin 2.0.0-RC3, we've made improvements to exception handling so that smart cast information can be passed on to catch and finally blocks. This change
makes your code safer as the compiler keeps track of whether your object has a nullable type. For example:

//sampleStart
fun testString() {
var stringInput: String? = null
// stringInput is smart cast to String type
stringInput = ""
try {
// The compiler knows that stringInput isn't null
println(stringInput.length)
// 0

// The compiler rejects previous smart cast information for


// stringInput. Now stringInput has the String? type.
stringInput = null

// Trigger an exception
if (2 > 1) throw Exception()
stringInput = ""
} catch (exception: Exception) {
// In Kotlin 2.0.0-RC3, the compiler knows stringInput
// can be null, so stringInput stays nullable.
println(stringInput?.length)
// null

// In Kotlin 1.9.20, the compiler says that a safe call isn't


// needed, but this is incorrect.
}
}
//sampleEnd
fun main() {
testString()
}

Increment and decrement operators


Prior to Kotlin 2.0.0-RC3, the compiler didn't understand that the type of an object can change after using an increment or decrement operator. As the compiler
couldn't accurately track the object type, your code could lead to unresolved reference errors. In Kotlin 2.0.0-RC3, this is fixed:

interface Rho {
operator fun inc(): Sigma = TODO()
}

interface Sigma : Rho {


fun sigma() = Unit

138
}

interface Tau {
fun tau() = Unit
}

fun main(input: Rho) {


var unknownObject: Rho = input

// Check if unknownObject inherits from the Tau interface


if (unknownObject is Tau) {

// Use the overloaded inc() operator from interface Rho,


// which smart casts the type of unknownObject to Sigma.
++unknownObject

// In Kotlin 2.0.0-RC3, the compiler knows unknownObject has type


// Sigma, so the sigma() function is called successfully.
unknownObject.sigma()

// In Kotlin 1.9.20, the compiler thinks unknownObject has type


// Tau, so calling the sigma() function throws an error.

// In Kotlin 2.0.0-RC3, the compiler knows unknownObject has type


// Sigma, so calling the tau() function throws an error.
unknownObject.tau()
// Unresolved reference 'tau'

// In Kotlin 1.9.20, the compiler mistakenly thinks that


// unknownObject has type Tau, so the tau() function is
// called successfully.
}
}

Kotlin Multiplatform improvements


In Kotlin 2.0.0-RC3, we've made improvements in the K2 compiler related to Kotlin Multiplatform in the following areas:

Separation of common and platform sources during compilation

Different visibility levels of expected and actual declarations

Separation of common and platform sources during compilation


Previously, due to its design, the Kotlin compiler couldn't keep common and platform source sets separate at compile time. This means that common code could
access platform code, which resulted in different behavior between platforms. In addition, some compiler settings and dependencies from common code were
leaked into platform code.

In Kotlin 2.0.0-RC3, we redesigned the compilation scheme as part of the new Kotlin K2 compiler so that there is a strict separation between common and platform
source sets. The most noticeable change is when you use expected and actual functions. Previously, it was possible for a function call in your common code to
resolve to a function in platform code. For example:

Common code Platform code

fun foo(x: Any) = println("common foo") // JVM


fun foo(x: Int) = println("platform foo")
fun exampleFunction() {
foo(42) // JavaScript
} // There is no foo() function overload
// on the JavaScript platform

In this example, the common code has different behavior depending on which platform it is run on:

On the JVM platform, calling the foo() function in common code results in the foo() function from platform code being called: platform foo

On the JavaScript platform, calling the foo() function in common code results in the foo() function from common code being called: common foo, since there is
none available in platform code.

In Kotlin 2.0.0-RC3, common code doesn't have access to platform code, so both platforms successfully resolve the foo() function to the foo() function in common

139
code: common foo

In addition to the improved consistency of behavior across platforms, we also worked hard to fix cases where there was conflicting behavior between IntelliJ IDEA
or Android Studio and the compiler. For example, if you used expected and actual classes:

Common code Platform code

expect class Identity { actual class Identity {


fun confirmIdentity(): String actual fun confirmIdentity() = "expect class fun:
} jvm"
}
fun common() {
// Before 2.0.0-RC3,
// it triggers an IDE-only error
Identity().confirmIdentity()
// RESOLUTION_TO_CLASSIFIER : Expected class
// Identity has no default constructor.
}

In this example, the expected class Identity has no default constructor, so it can't be called successfully in common code. Previously, only an IDE error was
reported, but the code still compiled successfully on the JVM. However, now the compiler correctly reports an error:

Expected class 'expect class Identity : Any' does not have default constructor

When resolution behavior doesn't change


We are still in the process of migrating to the new compilation scheme, so the resolution behavior is still the same when you call functions that aren't within the
same source set. You will notice this difference mainly when you use overloads from a multiplatform library in your common code.

For example, if you have this library, which has two whichFun() functions with different signatures:

// Example library

// MODULE: common
fun whichFun(x: Any) = println("common function")

// MODULE: JVM
fun whichFun(x: Int) = println("platform function")

If you call the whichFun() function in your common code, the function that has the most relevant argument type in the library is resolved:

// A project that uses the example library for the JVM target

// MODULE: common
fun main(){
whichFun(2)
// platform function
}

In comparison, if you declare the overloads for whichFun() within the same source set, the function from common code is resolved because your code doesn't have
access to the platform-specific version:

// Example library isn't used

// MODULE: common
fun whichFun(x: Any) = println("common function")

fun main(){
whichFun(2)
// common function
}

// MODULE: JVM
fun whichFun(x: Int) = println("platform function")

Similar to multiplatform libraries, since the commonTest module is in a separate source set, it also still has access to platform-specific code. Therefore, the

140
resolution of calls to functions in the commonTest module has the same behavior as in the old compilation scheme.

In the future, these remaining cases will be more consistent with the new compilation scheme.

Different visibility levels of expected and actual declarations


Before Kotlin 2.0.0-RC3, if you used expected and actual declarations in your Kotlin Multiplatform project, they had to have the same visibility level. Kotlin 2.0.0-RC3
supports different visibility levels only if the actual declaration is less strict than the expected declaration. For example:

expect internal class Attribute // Visibility is internal


actual class Attribute // Visibility is public by default,
// which is less strict

If you are using a type alias in your actual declaration, the visibility of the type must be less strict. Any visibility modifiers for actual typealias are ignored. For
example:

expect internal class Attribute // Visibility is internal


internal actual typealias Attribute = Expanded // The internal visibility
// modifier is ignored
class Expanded // Visibility is public by default,
// which is less strict

Compiler plugins support


Currently, the Kotlin K2 compiler supports the following Kotlin compiler plugins:

all-open

AtomicFU

jvm-abi-gen

js-plain-objects

kapt

Lombok

no-arg

Parcelize

SAM with receiver

Serialization

In addition, the Kotlin K2 compiler supports:

the Jetpack Compose 1.5.0 compiler plugin and later versions.

the Kotlin Symbol Processing (KSP) plugin since KSP2.

If you use any additional compiler plugins, check their documentation to see if they are compatible with K2.

Tasks error in Gradle configuration cache


Since Kotlin 2.0.0-Beta4, you may encounter a configuration cache error with messages indicating: invocation of Task.project at execution time is unsupported.

This error appears in tasks such as NativeDistributionCommonizerTask and KotlinNativeCompile.

However, this is a false-positive error. The underlying issue is the presence of tasks that are not compatible with the Gradle configuration cache, like the publish*
task.

This discrepancy may not be immediately apparent, as the error message suggests a different root cause.

As the precise cause isn't explicitly stated in the error report, the Gradle team is already addressing the issue to fix reports.

141
How to enable the Kotlin K2 compiler
Starting with Kotlin 2.0.0-Beta1, the Kotlin K2 compiler is enabled by default. No additional actions are required.

Try the Kotlin K2 compiler in Kotlin Playground


Kotlin Playground supports the 2.0.0-RC3 release. Check it out!

Support in IntelliJ IDEA

The K2 Kotlin mode is in Alpha. The performance and stability of code highlighting and code completion have been significantly improved, but not all IDE
features are supported yet.

Starting from 2024.1, IntelliJ IDEA can use the new K2 compiler to analyze your code with its K2 Kotlin mode. To try it out, go to Settings | Languages &
Frameworks | Kotlin and select the Enable the K2-based Kotlin plugin option.

Learn more about the K2 Kotlin mode in our blog.

We are actively collecting feedback about K2 Kotlin mode. Please share your thoughts in our public Slack channel!

Leave your feedback on the new K2 compiler


We would appreciate any feedback you may have!

Provide your feedback directly to K2 developers on Kotlin Slack – get an invite and join the #k2-early-adopters channel.

Report any problems you face with the new K2 compiler in our issue tracker.

Enable the Send usage statistics option to allow JetBrains to collect anonymous data about K2 usage.

Kotlin/JVM
This version brings the following changes:

Generate lambda functions using invokedynamic

The kotlinx-metadata-jvm library is now Stable

Generate lambda functions using invokedynamic


Kotlin 2.0.0-RC3 introduces a new default method for generating lambda functions using invokedynamic. This change reduces the binary sizes of applications
compared to the traditional anonymous class generation.

Since the first version, Kotlin has generated lambdas as anonymous classes. However, starting from Kotlin 1.5, the option for invokedynamic generation was
available by using the -Xlambdas=indy compiler flag. In Kotlin 2.0.0-RC3, invokedynamic has become the default method for lambda generation. This method
produces lighter binaries and aligns Kotlin with JVM optimizations, ensuring that applications benefit from ongoing and future improvements in JVM performance.

Currently, it has three limitations compared to ordinary lambda compilation:

A lambda compiled into invokedynamic is not serializable.

Experimental reflect() API does not support lambdas generated by invokedynamic.

Calling toString() on such a lambda produces a less readable string representation.

To retain the legacy behavior of generating lambda functions, you can either:

Annotate specific lambdas with @JvmSerializableLambda.

Use the compiler argument -Xlambdas=class to generate all lambdas in a module using the legacy method.

The kotlinx-metadata-jvm library is now Stable


In 2.0.0-RC3, the kotlinx-metadata-jvm library became Stable. Since the library's package and coordinates have changed to kotlin, you can now find it under the

142
name kotlin-metadata-jvm (without the "x").

Before, the kotlinx-metadata-jvm library had its own publishing scheme and version. Now, we build and publish the kotlin-metadata-jvm updates as part of the
Kotlin release cycle, with the same backward compatibility guarantees as the Kotlin standard library.

The kotlin-metadata-jvm library provides an API to read and modify metadata of binary files generated by the Kotlin/JVM compiler.

Kotlin/Native
This version brings the following changes:

Resolving conflicts with Objective-C methods

Changed log level for compiler arguments in Kotlin/Native

Resolving conflicts with Objective-C methods


Objective-C methods can have different names, but the same number and types of parameters. For example, locationManager:didEnterRegion: and
locationManager:didExitRegion:. In Kotlin, these methods have the same signature, so an attempt to use them triggers a conflicting overloads error.

Previously, you had to manually suppress conflicting overloads to avoid this compilation error. To improve Kotlin interoperability with Objective-C, the Kotlin 2.0.0-
RC3 introduces the new @ObjCSignatureOverride annotation.

The annotation instructs the Kotlin compiler to ignore conflicting overloads, in case several functions with the same argument types, but different argument names,
are inherited from the Objective-C class.

Applying this annotation is also safer than general error suppression. It allows you to use it only in the case of overriding Objective-C methods, which are supported
and tested, while general suppression may hide important errors and lead to silently broken code.

Changed log level for compiler arguments


In this release, the log level for compiler arguments in Kotlin/Native tasks, such as compile, link, and cinterop, changed from info to debug.

With debug as its default value, the log level is consistent with other compile tasks and provides detailed debugging information, including all compiler arguments.

Kotlin/Wasm
Kotlin 2.0.0-RC3 improves performance and interoperability with JavaScript:

Unsigned primitive types in functions with @JsExport

Binaryen available by default in production builds

Generation of TypeScript declaration files in Kotlin/Wasm

Support for named export

withWasm() function is split into JS and WASI variants

New functionality to catch JavaScript exceptions

The new exception handling proposal is now supported

Unsigned primitive types in functions with @JsExport


Kotlin 2.0.0-RC3 further improves the interoperability between Kotlin/Wasm and JavaScript. Now you can use unsigned primitive types inside external declarations
and functions with the @JsExport annotation that makes Kotlin/Wasm functions available in JavaScript code.

It helps to ease the previous limitation when generic class types couldn't be used directly inside exported declarations. Now you can export functions with unsigned
primitives as a return or parameter type and consume external declarations that return unsigned primitives.

For more information on Kotlin/Wasm interoperability with JavaScript, see the documentation.

Binaryen available by default in production builds

143
Kotlin/Wasm now applies WebAssembly's Binaryen library during production compilation to all the projects as opposed to the previous manual approach.

Binaryen is a great tool for code optimization. We believe it will improve your project performance and enhance your development experience.

This change only affects production compilation. The development compilation process stays the same.

Generation of TypeScript declaration files in Kotlin/Wasm

Generating TypeScript declaration files in Kotlin/Wasm is Experimental. It may be dropped or changed at any time.

In Kotlin 2.0.0-RC3, the Kotlin/Wasm compiler is now capable of generating TypeScript definitions from any @JsExport declarations in your Kotlin code. These
definitions can be used by IDEs and JavaScript tools to provide code autocompletion, help with type-checks, and make it easier to include Kotlin code in
JavaScript.

The Kotlin/Wasm compiler collects any top-level functions marked with @JsExport and automatically generates TypeScript definitions in a .d.ts file.

To generate TypeScript definitions, in your build.gradle.kts file in the wasmJs section, add the generateTypeScriptDefinitions() function:

kotlin {
wasmJs {
binaries.executable()
browser {
}
generateTypeScriptDefinitions()
}
}

Support for named export


Previously, all exported declarations from Kotlin/Wasm were imported into JavaScript using default export. Now, you can import each Kotlin declaration marked
with @JsExport by name:

// Kotlin:
@JsExport
fun add(a: Int, b: Int) = a + b

//JS:
import { add } from "./index.mjs"

Named exports make it easier to share code between Kotlin and JavaScript modules. They help improve readability and manage dependencies between modules.

withWasm() function is split into JS and WASI variants


The withWasm() function, which used to provide Wasm targets for hierarchy templates, is deprecated in favor of specialized withWasmJs() and withWasmWasi()
functions. Now you can separate the WASI and JS targets between different groups in the tree definition.

New functionality to catch JavaScript exceptions


Previously, Kotlin/Wasm code could not catch JavaScript exceptions, making it difficult to handle errors originating from the JavaScript side of the program. In
2.0.0-RC3, we have implemented support for catching JavaScript exceptions within Kotlin/Wasm.

This implementation allows you to use try-catch blocks, with specific types like Throwable or JsException, to handle these errors properly.

Additionally, finally blocks, which help execute code regardless of exceptions, also work correctly.

While we are introducing support for catching JavaScript exceptions, no additional information is provided when a JavaScript exception occurs, like a call stack.
However, we are working on these implementations.

The new exception handling proposal is now supported

144
In this release, we introduce support for the new version of WebAssembly's exception handling proposal within Kotlin/Wasm. With this update, Kotlin/Wasm gains
enhanced capabilities for managing exceptions.

The new exception handling proposal is activated using the -Xwasm-use-new-exception-proposal compiler option. It is turned off by default.

Additionally, we have ensured compatibility between the new compiler option and existing configurations, such as -Xwasm-use-traps-instead-of-exceptions.

Kotlin/JS
Among other changes, this version brings modern JS compilation to Kotlin, supporting more features from the ES2015 standard:

New compilation target

Suspend functions as ES2015 generators

Passing arguments to the main function

Per-file compilation for Kotlin/JS projects

Improved collection interoperability

Support for createInstance()

Support for type-safe plain JavaScript objects

Support for npm package manager

New compilation target


In Kotlin 2.0.0-RC3, we're adding a new compilation target to Kotlin/JS, es2015. This is a new way for you to enable all the ES2015 features supported in Kotlin at
once.

You can set it up in your build file like this:

kotlin {
js {
compilerOptions {
target.set("es2015")
}
}
}

The new target automatically turns on ES classes and modules and the newly supported ES generators.

Suspend functions as ES2015 generators


This release introduces Experimental support for ES2015 generators for compiling suspend functions.

We believe that using generators instead of state machines will improve both the final bundle size and your debugging experience.

Learn more about ES2015 (ECMAScript 2015, ES6) in the official documentation.

Passing arguments to the main function


Starting with Kotlin 2.0.0-RC3, you can specify a source of your args for the main() function. This feature makes it easier to work with the command line and pass
th