Kotlin
Kotlin is a multiplatform, statically typed, general-purpose programming language developed by JetBrains, targeting the Java Virtual Machine (JVM), JavaScript, and Native platforms, with a focus on conciseness, safety, and seamless interoperability with Java to enhance developer productivity in building server-side, mobile, web, and desktop applications.[1][2]
Initiated in 2010 by JetBrains lead designer Andrey Breslav as an alternative to Java's verbosity, Kotlin was publicly unveiled in July 2011, introducing features like function types and higher-order functions.[3][4] Key milestones include a major syntactic overhaul in 2012, the release of version 1.0 in February 2016 after years of open-source development, and the introduction of coroutines for asynchronous programming that same year.[5][4] Subsequent advancements encompassed Kotlin/Native preview in 2017, inline classes in 2018, and a shift to a six-month release cycle in 2020, alongside Kotlin Multiplatform Mobile reaching alpha status. Further developments include Kotlin Multiplatform reaching stable status in 2023, the major Kotlin 2.0 release in May 2024 introducing the stable K2 compiler, and Compose Multiplatform for iOS achieving stability in May 2025.[4][6][7][8]
Distinguished by its pragmatic design drawing influences from Java, Scala, C#, and Groovy, Kotlin emphasizes null safety through a type system that distinguishes nullable (?) and non-nullable types by default, reducing null pointer exceptions.[1][9] It supports data classes for concise boilerplate-free data holders that automatically generate equals(), hashCode(), and toString() methods, as well as extension functions to add methods to existing classes without modifying their source code.[9] Other notable features include smart casts for automatic type casting after checks, string templates for embedding expressions (e.g., "Hello, $name!"), and coroutines as lightweight alternatives to threads for handling concurrency.[9] Full interoperability with Java allows developers to call Kotlin code from Java and vice versa in the same project, with no performance overhead.[9]
Kotlin's adoption surged following Google's announcement of official support for Android development in 2017 and its designation as the preferred language for Android apps at Google I/O 2019, making it the default in Android Studio.[10][11] It now powers applications across platforms via Kotlin Multiplatform, targeting JVM, Android, iOS, macOS, Windows, Linux, and more, and is used by major organizations including McDonald's, AWS, Philips, Adobe, Forbes, and Atlassian for server-side, mobile, and data science projects.[2][4] As of November 2025, the latest stable release is Kotlin 2.0.20.[12]
History
Origins and Development
Kotlin's development was initiated by JetBrains in 2010 as an internal project aimed at creating a modern programming language for the Java Virtual Machine (JVM) that would mitigate common frustrations in Java development, such as excessive verbosity and the frequent occurrence of null pointer exceptions.[13] The language was designed to reduce boilerplate code while maintaining full interoperability with existing Java codebases, allowing for gradual adoption without requiring a complete rewrite of applications.[13] This effort was particularly motivated by the need for more concise and safe constructs in server-side and emerging mobile development contexts, including Android, where Java's limitations often led to cumbersome implementations.[14]
The project was led by Andrey Breslav, a language designer who joined JetBrains in 2010 specifically to head the initiative after prior experience in academia and at Borland working on language implementations.[13] Under his leadership, an initial prototype was developed over the following year, focusing on core features like type inference and null safety mechanisms to address Java's pain points directly on the JVM.[15] By mid-2011, after nearly a year of active internal development, the prototype was sufficiently advanced for public unveiling.[15]
JetBrains announced Project Kotlin publicly in July 2011 at the JVM Language Summit, presenting it as a statically typed, pragmatic language intended to enhance productivity for JVM developers.[15] The first public release of the compiler and related tools followed in early 2012, coinciding with the project's open-sourcing under the Apache 2.0 license in February of that year, which made the source code available on GitHub and encouraged community contributions.[14] This move aligned with JetBrains' goal of fostering a robust ecosystem around their IntelliJ IDEA tools, while emphasizing seamless JVM compatibility as a foundational principle from the outset.[14]
Naming and Early Milestones
The name "Kotlin" derives from Kotlin Island, a small landmass near Saint Petersburg, Russia, where much of the JetBrains team is based; this choice follows a tradition of drawing inspiration from geographical names to suggest compactness and potency, akin to the larger Java island in Indonesia.[13]
Kotlin was publicly unveiled by JetBrains at the JVM Language Summit on July 19, 2011, marking its first preview as a statically typed language targeting the Java Virtual Machine (JVM).[15] Over the subsequent years, the project incorporated extensive feedback from an active open-source community, iterating through milestones such as M5 in February 2013.[13] This collaborative refinement culminated in the release of Kotlin 1.0 Beta on November 2, 2015, after four years of development focused on stability and interoperability.[16]
A turning point for Kotlin's adoption came on May 17, 2017, when Google announced official support for the language in Android development during its Google I/O conference, designating it a first-class option alongside Java and integrating it directly into Android Studio.[10] This endorsement significantly boosted its visibility and usage in mobile ecosystems.
Despite these advances, early adoption encountered hurdles, including skepticism within the Java community about Kotlin's maturity, with common critiques centering on its pre-release status and the perceived risks of migrating from established Java codebases.[13]
Major Releases and Evolution
Kotlin reached production readiness with the stable release of version 1.0 on February 15, 2016, which included full interoperability with Java, support for coroutines in experimental form, and a robust standard library, enabling widespread adoption for JVM-based development.[5][17]
Subsequent releases focused on enhancing multiplatform capabilities and compiler performance. Kotlin 1.5.0, released on May 5, 2021, introduced a stable intermediate representation (IR)-based JVM compiler backend and improvements to multiplatform support, including better handling of common source sets and hierarchical project structures.[18] In 2022, Kotlin 1.8.0, released on December 28, 2022, marked the stabilization of the JavaScript IR compiler backend, advancing Kotlin's maturity for web development by providing more reliable and performant code generation for browser targets.[19]
The push toward full multiplatform maturity continued with Kotlin 1.9.20, released on November 1, 2023, which declared Kotlin Multiplatform stable and introduced support for Apple Silicon targets in Kotlin/Native, allowing native compilation optimized for ARM-based Apple hardware without emulation.[20][21] Kotlin 2.0.0, released on May 21, 2024, brought the K2 compiler to stable status across all targets, along with context receivers for more flexible DSLs and improved type inference for better developer productivity.[22]
In 2025, Kotlin 2.2.0, released on June 23, 2025, enhanced composability through the stabilization of features like guard conditions and non-local break/continue statements, while emphasizing performance optimizations in the K2 compiler and deprecating legacy elements such as certain outdated IR configurations to streamline modern usage.[23] Subsequent patch releases in the 2.2 series, including 2.2.20 on September 10, 2025, introduced Kotlin/Wasm to beta status with improvements to exception handling and web development support, and 2.2.21 on October 23, 2025, provided further compiler and backend fixes. As of November 2025, the latest stable version is 2.2.21, with Kotlin 2.3.0 in release candidate stage, focusing on feature stabilization and context-sensitive resolution enhancements.[12][24] These updates reflect Kotlin's ongoing evolution toward a fully mature multiplatform ecosystem, with brief references to targets like JVM, JS, and Native detailed further in multiplatform support documentation.[12]
Design Philosophy
Core Goals and Principles
Kotlin was designed as a pragmatic, statically typed programming language aimed at improving developer productivity in everyday software development, particularly by addressing common pain points in Java such as excessive boilerplate code and runtime errors from null references.[1] One of its primary goals is to reduce verbosity while maintaining full compatibility with the Java ecosystem, allowing developers to leverage existing libraries and frameworks without friction.[13] Central to this is enhanced null safety, achieved through a type system that distinguishes nullable (e.g., String?) and non-nullable types at compile time, thereby preventing null pointer exceptions that plague Java codebases.[25] This focus on safety extends to broader error prevention, making Kotlin suitable for large-scale applications where reliability is paramount.[26]
The language embodies a "pragmatic" philosophy, meaning it builds additively on Java's foundations rather than replacing it, ensuring 100% interoperability so that Kotlin code can call Java seamlessly and vice versa.[13] Conciseness is another core principle, prioritizing expressive syntax over Java's ceremonial style—for instance, through features like type inference and data classes that eliminate repetitive getter/setter implementations—without compromising performance through pragmatic trade-offs like efficient compilation to JVM bytecode.[25] This approach favors simplicity and readability, enabling maintainable code in expansive projects by minimizing cognitive load and encouraging idiomatic patterns that scale well in team environments.[27]
Influenced by languages like Scala and C#, Kotlin incorporates elements of functional programming (e.g., higher-order functions and lambdas) and object-oriented paradigms (e.g., properties and primary constructors) but deliberately avoids their complexities, such as Scala's implicit parameters or macros, to keep the language approachable and free of steep learning curves.[13] By supporting both paradigms in a unified, non-complex manner, Kotlin promotes versatility for diverse use cases, from Android apps to server-side development, while emphasizing long-term maintainability through clean, expressive code that fosters collaboration in large codebases.[1]
Interoperability with Existing Languages
Kotlin is designed for full interoperability with Java, compiling to the same JVM bytecode as Java, which allows Kotlin and Java code to be mixed seamlessly within the same project without requiring wrappers or adapters. This bytecode compatibility ensures that Kotlin classes can extend Java classes, implement Java interfaces, and override Java methods directly, while Java code can instantiate and invoke Kotlin classes as if they were native Java components. For instance, a Kotlin function can be called from Java by simply using the compiled class, leveraging the JVM's uniform execution model.[28][5]
Annotation processing and reflection are fully supported in Kotlin for Java libraries, enabling developers to use Java-based tools and frameworks without modification. The Kotlin compiler automatically converts Java APIs into more idiomatic Kotlin forms, such as transforming Java getters and setters into Kotlin properties, which simplifies access and reduces boilerplate. This includes support for Java's reflection APIs, allowing introspection of Kotlin code from Java and vice versa, as both operate on the same bytecode. Additionally, Kotlin provides built-in handling for Java's mutable collections, adapting them to its preference for immutability by offering extension functions that convert mutable Java structures like ArrayList to immutable Kotlin equivalents, such as List, while preserving mutability where needed through explicit APIs.[28][29]
Beyond the JVM, Kotlin extends interoperability to non-JVM targets. Through Kotlin/JS, which compiles Kotlin code to JavaScript for web environments, it allows direct calls to JavaScript libraries from Kotlin using external declarations marked with the external modifier to map to JavaScript APIs without runtime overhead. For example, developers can invoke JavaScript functions inline via the js() function or define type-safe wrappers for dynamic JavaScript objects, facilitating integration with existing web ecosystems like React or Node.js modules. This approach ensures that Kotlin code can leverage the vast JavaScript library landscape while maintaining compile-time checks where possible through dynamic types.[30][31]
Kotlin/Native provides interoperability with platform-specific languages, such as C for low-level systems programming and Objective-C/Swift for iOS development, enabling shared code across platforms while accessing native APIs directly.[32]
Syntax and Basic Features
Procedural and Entry-Point Syntax
Kotlin supports procedural programming through straightforward variable declarations and function definitions, enabling developers to write imperative code similar to other languages while incorporating modern features like type safety. Variables are declared using val for immutable bindings, which cannot be reassigned after initialization, promoting safer code by preventing accidental modifications.[33] In contrast, var declares mutable variables that can be reassigned, suitable for values that change during execution.[33] Both val and var leverage type inference, automatically determining the type from the initializer expression, which reduces boilerplate while maintaining compile-time type checking; for instance, val pi = 3.14 infers Double without explicit annotation.[33]
Functions in Kotlin are defined using the fun keyword, followed by the function name, parameter list in parentheses, and optional return type annotation separated by a colon.[34] Parameters are specified with their types, such as fun greet(name: [String](/page/String)), and can include default values to make arguments optional, as in fun describe(obj: Any = "Unknown") { println(obj) }, allowing calls like describe() without providing obj.[34] Return types are explicitly declared when needed, like : [Int](/page/INT) for numeric results, but omitted for functions returning [Unit](/page/Unit) (equivalent to void in other languages), with single-expression bodies using = for concise definition, e.g., fun sum(a: [Int](/page/INT), b: [Int](/page/INT)): [Int](/page/INT) = a + b.[34]
The standard entry point for a Kotlin program is the main function, declared as fun main(), which executes when the program starts.[33] This function can take an optional Array<String> parameter for command-line arguments, as in fun main(args: [Array](/page/Array)<String>) { println("Arguments: ${args.joinToString()}") }, providing a simple way to begin procedural execution without requiring a class wrapper, unlike Java.[33]
Kotlin's control structures emphasize expressiveness and safety in procedural flow. The if statement functions as both a statement and an expression, returning a value that can be assigned, such as val max = if (a > b) a else b, which eliminates the need for ternary operators.[33] The when construct serves as an enhanced switch, supporting multiple branches including type checks and ranges, for example:
when (x) {
1 -> "one"
in 2..10 -> "low number"
!is String -> "not a string"
else -> "unknown"
}
when (x) {
1 -> "one"
in 2..10 -> "low number"
!is String -> "not a string"
else -> "unknown"
}
This returns a value and requires exhaustive coverage in certain contexts to prevent runtime errors.[33] Loops include for for iterating over ranges or collections, like for (i in 1..5) print(i), while for condition-based repetition, such as while (condition) { /* body */ }, and do-while for post-condition checks.[33] Labels enable targeted breaks or continues across nested loops, prefixed with @, e.g., outer@ for (i in 1..3) { for (j in 1..3) { if (...) break@outer } }.[33]
Object-Oriented Constructs
Kotlin classes are declared using the class keyword followed by the class name, with the primary constructor optionally specified in the header after the class name in parentheses.[35] The primary constructor parameters can be turned into properties by prefixing them with val for read-only or var for mutable access, allowing direct access to the initialized values without additional boilerplate.[36] For instance:
kotlin
class Person(val name: [String](/page/String), var age: [Int](/page/INT))
class Person(val name: [String](/page/String), var age: [Int](/page/INT))
Here, name becomes a read-only property, while age is mutable.[36] Parameters without val or var are simply passed to the constructor but do not become properties.[36] Default values can also be assigned to parameters, enabling flexible instantiation.[36]
Inheritance in Kotlin requires explicit permission, as classes are final by default and cannot be subclassed unless marked with the open keyword.[37] To inherit from a class, the subclass uses a colon (:) followed by the superclass name and its constructor call if applicable.[38] Methods intended for overriding must also be marked open in the superclass, and the override in the subclass uses the override keyword.[38] An example demonstrates this:
kotlin
open class Base {
open fun draw() { /* ... */ }
}
class Derived : Base() {
override fun draw() { /* ... */ }
}
open class Base {
open fun draw() { /* ... */ }
}
class Derived : Base() {
override fun draw() { /* ... */ }
}
This design promotes safer extension by preventing unintended inheritance hierarchies.[37] The super keyword allows access to superclass members from within the subclass.[38]
Visibility in Kotlin is controlled by four modifiers: public (the default, accessible from anywhere), private (restricted to the declaring class or file), protected (accessible within the class and its subclasses), and internal (limited to the same module).[39] These apply to classes, functions, properties, and constructors, influencing encapsulation and module boundaries.[39] For example, a private function is only callable from within its declaring class:
kotlin
class Example {
private fun hidden() { /* ... */ }
}
class Example {
private fun hidden() { /* ... */ }
}
Protected members enable subclass access without exposing them broadly, while internal supports internal module APIs without public exposure.[39]
Interfaces in Kotlin, declared with the interface keyword, define contracts that classes can implement using the colon (:) syntax.[40] Unlike some languages, Kotlin interfaces can contain method implementations, providing default behaviors that implementing classes may override or use directly.[40] Properties in interfaces are abstract by default or can include custom accessors, but interfaces cannot hold state.[40] A basic example is:
kotlin
interface Clickable {
fun click()
fun showOff() { /* default implementation */ }
}
class Button : Clickable {
override fun click() { /* ... */ }
}
interface Clickable {
fun click()
fun showOff() { /* default implementation */ }
}
class Button : Clickable {
override fun click() { /* ... */ }
}
This allows multiple inheritance of behaviors through interfaces.[40]
Abstract classes, marked with the abstract keyword, serve as partial definitions that cannot be instantiated directly and are intended for inheritance.[41] They may include abstract members without implementations, which subclasses must override using override, alongside concrete members that are inherited as-is.[41] Subclasses must invoke the abstract class's constructor and provide implementations for all abstract elements.[41] For illustration:
kotlin
abstract class Shape {
abstract fun area(): Double
fun perimeter() = 0.0 // concrete member
}
class Circle(val radius: Double) : Shape() {
override fun area() = Math.PI * radius * radius
}
abstract class Shape {
abstract fun area(): Double
fun perimeter() = 0.0 // concrete member
}
class Circle(val radius: Double) : Shape() {
override fun area() = Math.PI * radius * radius
}
This construct facilitates shared code in base classes while enforcing extension for incomplete parts.[41]
Functional Programming Elements
Kotlin incorporates several functional programming paradigms to enable concise and expressive code, treating functions as first-class citizens and encouraging immutable data structures. This integration allows developers to leverage higher-order functions, lambdas, and immutability without sacrificing the language's object-oriented foundations.[1]
Lambdas in Kotlin serve as anonymous functions, defined using curly braces {} to enclose the parameter list, arrow ->, and body, where the last expression implicitly returns its value. For instance, a simple lambda adding one to its input is written as { it + 1 }, utilizing the implicit it parameter for single-argument cases, which simplifies syntax compared to explicit parameter naming. This feature supports functional composition by allowing lambdas to be passed directly to higher-order functions.[42]
Higher-order functions in Kotlin can accept functions as parameters or return them as results, facilitated by function types such as (Int) -> String, which denote input and output signatures. Standard library collections exemplify this through methods like map and filter: listOf(1, 2, 3).map { it * 2 } doubles each element, returning a new list, while listOf(1, 2, 3).filter { it > 1 } yields [2, 3], promoting declarative transformations over imperative loops. These operations create new collections rather than modifying existing ones, aligning with functional principles of avoiding side effects.[34][42]
Kotlin favors immutability to enhance code safety and predictability, using val for read-only declarations that prevent reassignment after initialization, in contrast to mutable var. Coding conventions explicitly recommend val for local variables and properties unless modification is required, reducing bugs from unintended changes. Similarly, collections default to read-only interfaces like List<T> and Set<T>, created via listOf() or setOf(), which support access operations but prohibit additions or removals; mutable variants require explicit mutableListOf() or toMutableList(). This design encourages treating data as immutable where possible, fostering thread-safety and easier reasoning about program state.[33][43][44][45]
To mitigate performance overhead from lambda allocations in higher-order functions—such as creating anonymous objects and closures—Kotlin provides inline functions via the inline modifier. When applied, the compiler substitutes the function body and its lambda arguments directly at the call site, eliminating runtime function object creation and virtual calls, which is particularly beneficial in performance-critical loops. For example, an inline repeat function avoids the closure cost of a non-inline version, though excessive inlining can increase compiled code size. This mechanism balances functional expressiveness with JVM efficiency.[46]
Advanced Language Features
Null Safety and Type System
Kotlin's type system is designed to be both expressive and safe, emphasizing static type checking to catch errors at compile time. A cornerstone of this system is its approach to nullability, which distinguishes between nullable and non-nullable types by default, reducing the incidence of NullPointerException (NPE) runtime errors that are common in languages like Java.[47] Non-nullable types, such as String, prohibit assignment of null and prevent null-related operations, while nullable types are explicitly declared with a trailing question mark, like String?, allowing null values.[47] This opt-in nullability model encourages developers to handle potential nulls intentionally, fostering safer code without excessive boilerplate.[47]
To interact with nullable types safely, Kotlin provides several operators. The safe call operator (?.) accesses properties or methods on a potentially null object, returning null if the receiver is null, rather than throwing an exception; for example, val length: Int? = nullableString?.length evaluates to null if nullableString is null.[47] The Elvis operator (?:) assigns a default value when the left-hand side is null, as in val len = nullableString?.length ?: 0, which uses 0 as a fallback.[47] For cases where null is guaranteed not to occur but the type checker cannot infer it, the not-null assertion operator (!!) forces a nullable type to non-nullable, though it throws an NPE if null is encountered, such as nullableString!!.length.[47] Additionally, when interoperating with Java, platform types arise because Java lacks nullability annotations; these are treated as nullable in Kotlin to avoid unchecked NPEs, requiring explicit handling like casting to String?.[47]
Kotlin enhances type safety through smart casts and sealed types. After an explicit type check, such as if (nullableString != null), the compiler smart casts the variable to its non-nullable form within that scope, allowing direct access like nullableString.length without additional assertions.[47] Sealed classes and interfaces restrict subclassing to known types within the same module, enabling exhaustive when expressions for pattern matching; for instance, a sealed class Result with subclasses Success and Error allows a when block to cover all cases without an else clause, ensuring completeness at compile time.[48]
The type system supports advanced generics with variance annotations to manage subtyping relationships. By default, generics are invariant, meaning List<String> is neither a subtype nor supertype of List<Any>.[49] Covariance is achieved with the out modifier for read-only projections, allowing List<out CharSequence> to be treated as List<out Any>, while contravariance uses in for write-only contexts, such as Comparator<in Any> being a supertype of Comparator<in String>.[49] Invariant generics remain unchanged, preserving full read-write capabilities.[49] Furthermore, reified type parameters in inline functions bypass type erasure, enabling runtime type checks; for example, an inline function inline fun <reified T> isA(value: Any) = value is T can use is T directly, which would otherwise be unavailable due to erasure in non-inline contexts.[49] This combination of features makes Kotlin's type system robust for building reliable, maintainable software.[49]
Extension Functions and Scope
Extension functions in Kotlin enable developers to add new methods to existing classes or interfaces without modifying their source code or relying on inheritance, promoting code reusability and extensibility especially for third-party libraries.[50] They are declared by prefixing the receiver type with a dot, followed by the function name, such as fun String.addExclamation(): String = this + "!".[50] When invoked, the extension function receives the original object as its implicit this parameter, allowing access to its public members; for example, "Hello".addExclamation() returns "Hello!".[50] Extensions can be defined at the top level, within companion objects, or as members of other classes, where member extensions include an additional explicit receiver for the enclosing class.[50] Resolution occurs statically at compile time based on the declared type, and member functions take precedence over extensions with the same signature.[50] Notably, extensions cannot access private or protected members of the receiver class and do not actually add members to the class at runtime.[50]
Kotlin provides a set of scope functions—let, run, with, apply, and also—that facilitate contextual execution of code blocks while providing convenient access to an object reference, reducing boilerplate and improving readability in scenarios like object configuration or chaining operations.[51] These functions create a temporary scope where the object is available either as this (for run, with, and apply) or it (for let and also), allowing method calls and property access without qualification.[51] The let function is ideal for executing code on non-null results or transforming values, returning the lambda's result; for instance, val lengths = strings.map { it.length }.let { println(it); it.[filter](/page/Filter) { it > 3 } }.[51] In contrast, run combines initialization and computation, also returning the lambda result, as in val service = [Service](/page/Service)().run { port = 8080; query("request"); this }.[51] The with function, which requires the object as an argument, suits non-receiver calls focused on computation, such as with(numbers) { println(size); sum() }.[51]
For object configuration, apply and also return the context object itself, enabling fluent chaining; apply uses this for property assignments like val adam = Person("Adam").apply { age = 32; city = "London" }, while also employs it for side effects, e.g., numbers.also { println("The list size is ${it.size}") }.add("four").[51] Choosing among these depends on whether the focus is on the result (let, run, with) or the object (apply, also), and the preferred reference style (this vs. it).[51] Scope functions can be safely used with nullable receivers via safe calls, briefly referencing null safety mechanisms to avoid null pointer exceptions.[51]
Destructuring declarations allow unpacking the components of an object, such as a pair or data class, into multiple variables in a single statement, enhancing conciseness when working with structured data.[52] The syntax val (name, age) = person declares name and age by invoking person.component1() and person.component2(), respectively, assuming the class defines these functions (automatically generated for data classes).[52] This extends to loops over collections, where for ((a, b) in [map](/page/Map)) { println("$a = $b") } iterates by destructuring each key-value pair.[52] In lambdas, destructuring simplifies parameter handling, as in mapOf(1 to "one", 2 to "two").forEach { (key, value) -> println("$key = $value") }.[42]
The spread operator * unpacks an array or collection into vararg arguments, enabling functions expecting multiple parameters to receive elements individually; for example, fun asList(vararg numbers: Int): List<Int> { ... } can be called as asList(-1, 0, *arrayOf(1, 2, 3), 4).[34] This is particularly useful for passing array contents without manual expansion, though it involves array creation for safety.[34]
Nested and local functions support encapsulation by defining functions inside other functions, allowing helper logic to be scoped locally and access variables from the enclosing scope for closure-like behavior.[34] A local function is declared with fun within another function and can capture outer variables, as in:
kotlin
fun numCopies(x: Int): (Int) -> Int {
fun copy(y: Int): Int = y * x // Captures x
return copy
}
fun numCopies(x: Int): (Int) -> Int {
fun copy(y: Int): Int = y * x // Captures x
return copy
}
This promotes modular code without polluting the global namespace.[34] Local functions are hoisted within their scope but cannot be recursive unless explicitly labeled, and they enhance readability by grouping related computations.[34]
Coroutines and Concurrency
Kotlin coroutines provide a lightweight concurrency framework that enables asynchronous programming without the overhead of traditional threads, allowing developers to write non-blocking code in a sequential manner.[53] Introduced in Kotlin 1.3 as part of the kotlinx.coroutines library, coroutines are built around the concept of suspendable computations, which pause execution at suspension points without blocking the underlying thread.[53] This model supports efficient handling of I/O-bound and CPU-bound tasks, such as network requests or computations, by leveraging cooperative multitasking.[54]
At the core of coroutines are suspend functions, marked with the suspend modifier, which can only be invoked from other suspend functions or within a coroutine scope.[54] For example, a suspend function might use delay(1000) to pause for one second without blocking the thread, enabling other coroutines to proceed.[54] Coroutine builders like launch and async initiate these computations within a CoroutineScope, which manages their lifecycle. The launch builder starts a coroutine that does not return a value, suitable for fire-and-forget tasks, while async returns a Deferred object to await a result later, facilitating concurrent execution of multiple operations.[54]
Structured concurrency ensures that coroutines form a hierarchy where child coroutines are tied to their parent's lifecycle, preventing resource leaks from orphaned tasks.[55] The coroutineScope function creates such a scope, launching child coroutines that complete before the scope finishes; if any child fails, the entire scope cancels, propagating exceptions to cancel siblings.[55] Cancellation is cooperative and checked at suspension points, with supervision available through supervisorScope to isolate failures in individual children without affecting the parent.[56]
For inter-coroutine communication, channels offer a non-blocking alternative to queues, supporting actor-like patterns where producers send values via send and consumers receive via receive.[57] Built with the Channel class, they can be buffered or unbuffered, and builders like produce create producer coroutines that emit sequences, consumable in a for-loop until closed.[57] Complementing channels, Flows provide a reactive stream API for handling asynchronous sequences of values, defined as cold streams using the flow builder with emit for values.[58] Flows support operators like map and filter for transformation and are collected sequentially in the caller's coroutine, with flowOn for dispatcher switching.[58]
Coroutines integrate with Java's concurrency utilities by converting an ExecutorService to a CoroutineDispatcher via asCoroutineDispatcher(), allowing existing thread pools to schedule coroutines.[59] This enables gradual migration, where coroutines can run on custom executors, including scheduled ones for time-based operations like delay.[59] Regarding multiplatform support, kotlinx.coroutines provides stable core features across JVM, JS, and Native targets, with some advanced APIs marked as experimental using @ExperimentalCoroutinesApi.[60]
Kotlin Multiplatform (KMP) is an open-source technology developed by JetBrains that enables developers to reuse Kotlin code across multiple platforms, including JVM, Native, JavaScript, and emerging targets like WebAssembly, while preserving native performance and access to platform-specific APIs.[61] First announced at KotlinConf 2017 as "Kotlin Multiplatform Projects," it evolved from experimental multi-target capabilities in Kotlin 1.2 to address the need for efficient code sharing in cross-platform development.[62] In November 2023, KMP achieved stable and production-ready status, marking a significant milestone that solidified its role in industrial applications.[6] As of Kotlin 2.2.0 in June 2025, further enhancements include improved compatibility with Gradle 8.x and experimental WebAssembly support for broader web targeting.[23]
The foundation of KMP lies in its hierarchical project structure, centered on shared modules that contain platform-agnostic business logic, such as data processing, validation rules, and networking abstractions.[63] Developers organize code into source sets: a "common" source set for shared elements and platform-specific source sets (e.g., for Android or iOS) for tailored implementations. This setup promotes modularity, allowing teams to maintain a single codebase for core functionality while adapting to each platform's ecosystem.[64]
A key mechanism in KMP is the expect/actual declaration system, which facilitates safe integration of platform differences. In the common module, an "expect" declaration outlines the expected API signature—such as a function or class—without providing an implementation, ensuring type safety across targets. Each platform module then supplies one or more "actual" declarations that fulfill these expectations, often by delegating to native libraries or frameworks; for example, an expect function for file I/O might map to Java's File API on JVM and POSIX calls on Native. This approach minimizes boilerplate and errors, enabling seamless compilation to diverse outputs.[65]
By focusing on business logic in shared modules, KMP delivers substantial benefits in code reuse, potentially achieving up to 100% sharing for non-UI elements like algorithms and domain models, which reduces duplication and maintenance overhead in projects spanning mobile, desktop, and web applications.[61] For instance, a financial app could share transaction validation logic across Android, iOS, and a web client, streamlining feature rollouts and cutting development time. However, limitations persist: user interfaces and low-level platform APIs, such as hardware sensors or OS-specific services, generally require separate implementations to ensure optimal integration and user experience. Coroutines are fully supported in KMP for handling concurrency in shared code, providing a unified asynchronous programming model across platforms.
JVM and Android Targeting
Kotlin primarily targets the Java Virtual Machine (JVM) by compiling source code into JVM bytecode, enabling seamless interoperability with existing Java ecosystems. This compilation process allows Kotlin applications to run on any JVM implementation, such as OpenJDK or Oracle JDK, and provides full access to Java libraries and frameworks without requiring wrappers or adapters. For instance, Kotlin integrates natively with enterprise frameworks like Spring Boot, where developers can leverage Kotlin's concise syntax alongside Spring's dependency injection and web capabilities.[5][28][66]
In Android development, Kotlin's JVM targeting has become central since Google announced official first-class support in May 2017 at Google I/O, integrating it directly into Android Studio and the build toolchain. This endorsement facilitated Kotlin's rapid adoption, with over 50% of professional Android developers using Kotlin as their primary language as of October 2025, and 70% of the top 1000 apps on Google Play written in Kotlin.[67][68] The language's bytecode output aligns perfectly with Android Runtime (ART), allowing apps to benefit from just-in-time (JIT) and ahead-of-time (AOT) compilation optimizations provided by the platform. Within the broader Kotlin Multiplatform framework, JVM-targeted code can be shared across Android and other JVM-based environments.[69][70]
Kotlin includes several JVM-specific optimizations to enhance efficiency and reduce overhead. Inline classes, declared with the @JvmInline annotation, wrap primitive or other value types to provide type safety without runtime allocation costs, as the compiler inlines the underlying value during bytecode generation. Similarly, Kotlin properties are desugared into standard JVM getter and setter methods in the bytecode; for example, a simple var property automatically generates a backing field and accessor methods, ensuring compatibility with Java reflection and serialization tools while avoiding direct field access. These features maintain conceptual equivalence to Java constructs but leverage Kotlin's type system for safer, more expressive code.[71]
Kotlin achieves performance parity with Java on the JVM, as both languages compile to the same bytecode and benefit from identical JIT optimizations, with benchmarks showing negligible differences in execution time for typical workloads. In scenarios involving lambdas or higher-order functions, Kotlin's inline mechanisms can even yield slight improvements by eliminating virtual calls. For performance-critical Android components interfacing with native code via the Native Development Kit (NDK), Kotlin/Native enables ahead-of-time (AOT) compilation to machine code, producing standalone binaries that integrate as shared libraries without JVM overhead.[72][32]
Native and JavaScript Targets
Kotlin/Native compiles Kotlin code to standalone native binaries using an LLVM-based backend, enabling execution without a virtual machine on platforms where JVMs are unsuitable or unavailable.[32] This approach supports a range of operating systems, including Tier 1 targets like iOS (iosArm64, iosX64), macOS (macosX64, macosArm64), and Linux (linuxX64, linuxArm64), where full compilation, linking, and testing are reliably supported.[73] Since 2023, Kotlin/Native has achieved greater stability, particularly achieving full stability for Apple Silicon targets in November 2023 with Kotlin 1.9.20, allowing production use for cross-platform applications.[74] Memory management relies on a tracing garbage collector (GC) that operates on a shared heap accessible from multiple threads, automatically reclaiming unreachable objects based on roots such as local variables and globals, with periodic cycles triggered by heuristics or timers.[75]
A primary use case for Kotlin/Native is developing iOS applications through Kotlin Multiplatform (KMP), where shared business logic can be compiled into frameworks consumable by Swift or Objective-C, leveraging expect/actual declarations to handle platform-specific implementations.[32] However, limitations persist in GC behavior; while the collector supports concurrent marking to reduce pauses, full concurrency is not available on all targets, and integration with Swift's Automatic Reference Counting (ARC) requires careful handling to avoid issues during GC pauses, such as prohibiting arbitrary Kotlin code execution in deinit blocks.[76] In 2025, updates to Kotlin/Native include enhancements to GC responsiveness, such as shortened pause times in version 2.0.20 and concurrent marking improvements for better threading performance, alongside optimizations for build times through compiler internals and safer caching mechanisms.[77][78]
Kotlin/JS transpiles Kotlin code, the standard library, and dependencies to JavaScript, producing ES modules compatible with modern browsers and Node.js environments.[31] The IR (Intermediate Representation) backend, which enables these optimizations, reached stable status in August 2022 with Kotlin 1.8.0, providing polyfills equivalent to the legacy backend for broader compatibility.[79] Interoperability with npm packages is facilitated through external declarations, allowing Kotlin to import JavaScript libraries via annotations like @JsModule and dynamic types for seamless integration.[30]
Common use cases for Kotlin/JS include serverless web applications, where shared logic reduces duplication between frontend and backend, and browser-based clients that benefit from type-safe Kotlin constructs transpiled to efficient JavaScript.[31] In 2025, the IR backend saw further optimizations, including support for the latest JavaScript standards (beyond ES5) with features like classes and generators, stabilized reduced bundle sizes for faster loads, and enhanced exports for suspend functions, value classes, and type aliases to improve performance and type definitions in .d.ts files.[80][78]
Integrated Development Environments
Kotlin has enjoyed native integration with IntelliJ IDEA since its inception in 2011, as JetBrains developed the language specifically for their IDE ecosystem, providing seamless support for Kotlin development from the outset.[81] This integration includes advanced features such as smart code completion, which offers context-aware suggestions based on Kotlin's type system and idioms, and comprehensive refactoring tools that handle safe transformations like renaming or extracting functions while preserving null safety and multiplatform compatibility.[82] Additionally, IntelliJ IDEA supports specialized debugging for Kotlin coroutines, allowing developers to step through asynchronous code, inspect suspension points, and visualize coroutine hierarchies to diagnose concurrency issues efficiently.[83]
Android Studio, built on the IntelliJ platform, gained full official support for Kotlin following its announcement at Google I/O 2017, with the language integrated as a first-class option starting in Android Studio 3.0.[84] This support encompasses Kotlin-aware code inspections that detect issues like platform type mismatches or deprecated usage in Android contexts, along with refactoring capabilities tailored to Android development.[81] For multiplatform projects, Android Studio includes wizards and templates that facilitate the creation of shared modules, enabling code reuse across Android, iOS, and other targets through Kotlin Multiplatform configurations.[85]
Support for other editors, such as Visual Studio Code, is available through official plugins leveraging the Kotlin Language Server Protocol (LSP), which was introduced by JetBrains in 2025 to provide features like syntax highlighting, error diagnostics, and code navigation in a lightweight environment.[86] These plugins enable interactive development without the full IntelliJ overhead, complementing the standalone Kotlin compiler's REPL mode via the kotlinc tool, which allows for rapid prototyping and script execution in an interactive shell.[87]
In 2025, JetBrains enhanced Kotlin tooling with AI-assisted features in their IDEs, including the AI Assistant plugin for IntelliJ IDEA and Android Studio, which generates code snippets, refactors Kotlin structures, and provides contextual explanations powered by large language models.[88] These updates, highlighted at KotlinConf 2025, also introduce Junie, an AI agent for automated task handling like commit message generation and multiplatform setup, improving productivity for Kotlin developers across platforms.[89]
Build Systems and Compilation
The Kotlin compiler, known as kotlinc, is the core tool for compiling Kotlin code across multiple targets, including the Java Virtual Machine (JVM), JavaScript (JS), and native binaries for various platforms.[90] It processes Kotlin source files into platform-specific outputs, such as JVM bytecode, JavaScript modules, or native executables via an LLVM-based backend for Kotlin/Native.[32] The compiler supports standalone invocation from the command line, with each Kotlin release providing binaries tailored to these targets, enabling developers to build applications without relying on a virtual machine for native execution.[87]
A key feature enhancing compilation efficiency is incremental compilation, which tracks changes in source files and dependencies between builds to recompile only affected modules, significantly reducing build times.[91] Introduced in earlier versions and refined in subsequent updates, this mechanism leverages caches in the Kotlin daemon to avoid redundant processing, particularly beneficial for large projects where full recompilations can be time-intensive.[92]
For build automation, the Kotlin Gradle plugin serves as the primary tool, integrating seamlessly with Gradle's ecosystem to manage dependencies, compilation, and packaging for JVM, Android, JS, and native targets.[93] Developers apply the plugin via the plugins block in build.gradle.kts files, specifying targets and configurations to automate the build process across platforms.[94] Maven support is also available through the Kotlin Maven plugin, allowing compilation in Maven-based projects by defining the plugin in pom.xml and specifying the Kotlin version.[95] In Kotlin Multiplatform projects, build scripts use the Kotlin Multiplatform Gradle plugin to define shared and platform-specific modules, enabling unified configuration for cross-target compilation.[96]
Compilation occurs in distinct modes to suit development and production needs: debug mode preserves debugging information and disables optimizations for easier troubleshooting, while release mode applies optimizations for smaller, faster executables without debug symbols.[90] These modes are configured via compiler options like -Xdebug or through build script settings in Gradle and Maven. In Kotlin Multiplatform (KMP), cross-compilation allows building libraries or applications for multiple targets from a single host machine, producing artifacts like Kotlin libraries (.klib) that can be consumed across platforms without host-specific recompilation.[97]
As of 2025, Kotlin/Native builds have seen improvements in speed through enhanced caching mechanisms, where the compiler stores intermediate results in the ~/.konan directory to minimize redundant downloads and computations during iterative development.[98] For Kotlin/JS, JetBrains announced plans to refine webpack integration to support modern bundlers like esbuild or Vite, decoupling the default webpack configuration.[80] These planned updates emphasize reduced build times and broader toolchain compatibility while maintaining compatibility with existing JVM and native backends.
Standard Library and Third-Party Resources
The Kotlin Standard Library (stdlib) serves as the foundational set of APIs for Kotlin development, offering essential utilities for common programming tasks across platforms. It includes comprehensive support for collections through the kotlin.collections package, providing interfaces like Iterable, List, Set, and Map, along with higher-order functions for operations such as filtering, mapping, and folding. For input/output operations, the kotlin.io package extends Java's file and stream APIs with Kotlin-idiomatic extensions, enabling concise handling of paths, files, and buffering. Additionally, the kotlin.coroutines package integrates coroutine fundamentals, including Continuation and CoroutineContext, to facilitate asynchronous programming without external dependencies.
In multiplatform contexts, the standard library features platform-agnostic subsets that ensure shared code compatibility. The common source set includes core types and functions available across JVM, JavaScript, and Native targets, while platform-specific extensions (e.g., kotlin.js.collections for web) handle unique behaviors. This design allows developers to write reusable logic while accessing native capabilities when needed.[63]
Beyond the standard library, the Kotlin ecosystem thrives on third-party resources that extend its capabilities for specialized domains. Ktor, developed by JetBrains, is a lightweight framework for building asynchronous servers and clients, leveraging coroutines for efficient HTTP handling and microservices.[99] Exposed provides a type-safe DSL for SQL queries and a lightweight ORM for database interactions, supporting JDBC and R2DBC drivers.[100] For functional programming paradigms, Arrow offers typed abstractions like Either and Option, along with optics and concurrency tools, complementing Kotlin's expressive syntax.[101] In Android development, Koin simplifies dependency injection with a declarative DSL, enabling modular component wiring across modules.[102] Compose Multiplatform, also from JetBrains, enables declarative UI development shared across Android, iOS, desktop, and web, building on Jetpack Compose for native performance and interoperability.
The Kotlin ecosystem has expanded significantly, with thousands of libraries available on Maven Central, fostering innovation in areas like networking, data persistence, and UI. Dependency management is primarily handled through Gradle, which integrates seamlessly with Kotlin projects for resolving and versioning libraries. Kotlin enforces binary compatibility guarantees for public and protected APIs in the standard library across minor releases (e.g., 1.x to 1.y), ensuring that compiled code remains interchangeable without recompilation, while major versions may introduce changes.[26]
Adoption and Impact
Industry Usage and Case Studies
Kotlin has achieved significant dominance in Android development, with 95% of the top 1,000 Android apps incorporating Kotlin code as of 2024.[103] Companies like Pinterest began adopting Kotlin for their Android app in 2017, leading to a 50% decrease in crash rates and enabling more concise implementations of complex features such as image recognition and personalized recommendations.[104] Square (now part of Block) was an early adopter of Kotlin for Android starting in 2015, leveraging it to enhance developer libraries like OkHttp and improve overall app performance and maintainability.[105] Netflix migrated its Android app to Kotlin in phases, decoupling nearly 50% of production code from platform-specific details to streamline updates and reduce duplication.[106]
On the server-side, Kotlin's adoption for backend development has grown steadily, with experienced users nearly tripling since 2021 according to developer surveys.[107] Uber employs Kotlin extensively for microservices and internal tools, including large-scale migrations of millions of lines of Java code to Kotlin using AI-assisted tools, resulting in 80% of modifications being automated.[108] Atlassian utilizes Kotlin for developing Jira Software cloud products, decomposing monolithic applications into microservices while benefiting from its interoperability with existing Java codebases.[109]
In multiplatform scenarios, Kotlin enables code reuse across platforms, as demonstrated by Netflix's implementation of shared business logic modules for its Android and iOS studio apps, which reduced development duplication and improved reliability.[110] This approach allows a single codebase for core functionality, with platform-specific UI handled natively, accelerating feature delivery in cross-platform environments.[106]
Migrating from Java to Kotlin presents challenges such as initial learning curves and integration with legacy code, which can increase short-term development costs.[111] However, the benefits include enhanced productivity, with Kotlin code often 20-40% shorter than equivalent Java implementations, leading to faster development cycles and fewer bugs.[112] For instance, developers report writing clearer, more concise code that reduces maintenance overhead in production systems.[113]
Community Contributions and Future Directions
The Kotlin programming language's official GitHub repository, maintained by JetBrains, has garnered over 51,800 stars, reflecting widespread community interest and contributions to its development.[114] In 2019, the Kotlin Foundation was established as a non-profit organization by JetBrains and Google to govern the language's evolution, protect its trademark, and foster collaborative advancements, with additional members including Meta, Uber, and Gradle joining over time.[115][116]
The community actively participates in language evolution through KotlinConf, an annual conference organized by JetBrains that brings developers together to share innovations, network, and explore Kotlin's applications across platforms.[117] Contributions to the language's design occur via the Kotlin Evolution and Enhancement Proposals (KEEPs), a formal process hosted on GitHub where community members submit, review, and refine ideas for new features, ensuring transparent and expert-vetted progress.[118]
Looking ahead, the 2025 roadmap emphasizes stabilizing Compose Multiplatform for desktop and web targets, with web support reaching Beta status to enable production-ready cross-platform UIs.[119][120] Enhanced AI integration is a priority, particularly in development tools like IntelliJ IDEA, to provide Kotlin-specific assistance for code generation and optimization, as highlighted at KotlinConf 2025.[89] The Kotlin/Wasm target is advancing toward broader adoption, allowing Kotlin code to run efficiently in web browsers and potentially expanding multiplatform capabilities.[80] By 2025, the language supports over 2.5 million developers worldwide, with ongoing efforts to address interoperability gaps, such as improved integration with Rust via community-driven FFI tools and conference discussions.[89][121]