Kotlin — Reference

Source: https://kotlinlang.org/docs/home.html

Kotlin

  • Created: 2011 (announced by JetBrains; 1.0 released February 2016)
  • Latest stable: 2.3.21 (2026-04, bug-fix on the 2.3.20 tooling release; 2.3.0 language release was 2025-12)
  • Paradigms: Multi-paradigm — object-oriented, functional, imperative; coroutines for structured concurrency
  • Typing: Static, strong, with type inference; null-safety baked in (T vs T?)
  • Memory: Garbage-collected on JVM/Android; Kotlin/Native uses a tracing GC since the “new memory manager” (1.7.20+) replacing the legacy reference-count + cycle collector
  • Compilation: AOT to JVM bytecode (default), JS, Wasm (alpha/beta), and native via LLVM (Kotlin/Native). K2 compiler became default in 2.0.0 (2024-05)
  • Primary domains: Android (Google’s preferred language since 2019), server-side (Ktor, Spring), multiplatform mobile/desktop/web (KMP), build tooling (Gradle Kotlin DSL)
  • Official docs: https://kotlinlang.org/docs/home.html

At a glance

  • Owner / steward: JetBrains (with the Kotlin Foundation, co-founded with Google).
  • Designed to be 100% interoperable with Java; targets JVM 1.8+ by default, with separate backends for Android, JS, Wasm, and native (LLVM).
  • Release cadence (since 2.0.0): language releases (2.x.0) every ~6 months, tooling releases (2.x.20) 3 months later, bug-fixes ad-hoc. (https://kotlinlang.org/docs/releases.html)
  • LTS-style stability: K2 compiler stable in 2.0.0; binary compatibility across 2.x is strong on JVM.
  • Major runtimes: HotSpot/OpenJDK, GraalVM, Android ART, Kotlin/Native (LLVM), Kotlin/JS (IR backend), Kotlin/Wasm.

Getting started

  • Install: SDKMAN! (sdk install kotlin), Homebrew (brew install kotlin), Scoop, Chocolatey, or via IntelliJ IDEA / Android Studio bundle. For multiplatform, use the Kotlin Multiplatform Wizard or IntelliJ. (https://kotlinlang.org/docs/command-line.html)

  • Version manager: SDKMAN! is the de-facto choice on macOS/Linux; on Windows, Scoop or kotlinc bundled with the IDE. No “rustup-style” official switcher.

  • Hello world (hello.kt):

    fun main() = println("Hello, world!")

    Compile + run: kotlinc hello.kt -include-runtime -d hello.jar && java -jar hello.jar. Or use kotlin hello.kt (script mode), or kotlin -script hello.kts.

  • Project layout (Gradle / Kotlin DSL): build.gradle.kts, settings.gradle.kts, sources in src/main/kotlin/, tests in src/test/kotlin/. The Kotlin Multiplatform layout adds commonMain, jvmMain, nativeMain, etc.

  • Build tool: Gradle is canonical (Kotlin DSL preferred over Groovy DSL); Maven and Bazel also supported. Amper (JetBrains’ newer tool, experimental as of 2026) is gaining traction for KMP.

  • REPL: kotlinc-jvm opens the REPL; in IntelliJ use Tools → Kotlin → Kotlin REPL. ki (Kotlin Interactive Shell) is a more advanced JetBrains-maintained REPL with autocompletion. (https://kotlinlang.org/docs/command-line.html#run-the-repl)

Basics

  • Types/literals: Int, Long, Double, Float, Boolean, Char, String; unsigned types (UInt, ULong — stable since 1.5). Numeric literals: 1_000_000, 0xFF, 0b1010, 1.5e3, 1L, 1u.
  • Variables: val (immutable binding), var (mutable). lateinit var for late-initialized non-null. Property delegation: by lazy {}, by Delegates.observable(...).
  • Scoping: Lexical, block-scoped. Top-level val/fun allowed (no class wrapper required, unlike Java).
  • Control flow: if/when are expressions (not just statements). when does pattern-like matching with is, in, ranges. for (x in 1..10), while, do-while. Labeled break@loop/continue@loop/return@lambda.
  • Functions: fun add(a: Int, b: Int): Int = a + b. Default args, named args, vararg, single-expression body, top-level functions, extension functions (fun String.shout() = uppercase() + "!"), infix, operator overloading.
  • Strings: Double-quoted with $var / ${expr} interpolation. Triple-quoted raw strings """...""" with .trimIndent() / .trimMargin().
  • Collections: listOf, mutableListOf, setOf, mapOf, arrayOf, plus primitive arrays (IntArray). Sequence API for lazy chains. The collections framework distinguishes read-only interfaces (List, Set, Map) from mutable ones — the JVM impl is still a java.util.ArrayList, so the immutability is compile-time only.

Intermediate

  • Type system depth: nullable types (T?), safe call (?.), Elvis (?:), not-null assert (!!), smart casts after is checks. Generics with declaration-site variance (out T, in T) and use-site variance. Type aliases.
  • Sealed class/interface hierarchies for closed sums; data class for value semantics (equals/hashCode/copy/componentN synthesized); value class (formerly inline class) for zero-overhead wrappers.
  • Modules: Source-set based via Gradle/IntelliJ. Visibility modifiers: public (default), internal (module-scoped), protected, private. Java’s JPMS (module-info.java) is supported on JVM since Kotlin 1.2.
  • Error handling: Exceptions only — Kotlin has no checked exceptions. try/catch/finally is an expression. runCatching {} returns a Result<T>. throw is also an expression.
  • Concurrency: kotlinx.coroutines is the standard answer — suspend fun, launch, async/await, Flow (cold async streams), StateFlow/SharedFlow. Structured concurrency via CoroutineScope/SupervisorJob. Channels and select {} for CSP-style. Threads available via java.lang.Thread on JVM.
  • I/O: On JVM, the full Java NIO/IO is available. kotlinx-io is the multiplatform I/O library (still pre-1.0 as of 2026).
  • Stdlib highlights: kotlin.collections (map, filter, fold, groupBy, windowed, zipWithNext, chunked), scope functions (let, run, with, apply, also), kotlin.text (regex, builders), kotlin.time (Duration, TimeSource, measureTime), kotlin.io (file helpers), Result<T> for failure handling.

Advanced

  • Memory/GC: JVM uses whichever GC the JVM is configured for (G1 default; ZGC/Shenandoah for low pause). Kotlin/Native shipped a new memory manager in 1.7.20 (stable 1.9) that replaced the strict ownership model with a tracing concurrent GC, removing the previous “frozen object” pain. (https://kotlinlang.org/docs/native-memory-manager.html)
  • Concurrency deep dive: Coroutines compile to a continuation-passing-style state machine — every suspend function gets an extra Continuation parameter. Dispatchers.Default is a fork-join pool sized to CPU count; Dispatchers.IO is an elastic 64+ thread pool. Flow operators are cold and run on the collector’s context unless flowOn shifts upstream. (https://kotlinlang.org/docs/coroutines-overview.html)
  • FFI: Kotlin/Native uses cinterop to consume C/Objective-C headers (generates a .def file → Kotlin bindings). On JVM, use Java’s JNI, JNA, or Project Panama (java.lang.foreign). No FFI on Kotlin/JS (use external/dynamic).
  • Reflection: kotlin-reflect artifact (separate jar, ~3 MB) gives KClass, KFunction, KProperty with full Kotlin metadata. Java reflection still works but loses Kotlin nuances (default args, nullability, data class synthetics).
  • Performance tools: YourKit, JProfiler, async-profiler on JVM; IntelliJ Profiler (built on async-profiler since 2023). For coroutines, the kotlinx-coroutines-debug agent + DebugProbes.dumpCoroutines(). JMH for microbenchmarks via the kotlinx.benchmark plugin (multiplatform).

God mode

  • KSP (Kotlin Symbol Processing): Kotlin’s modern annotation-processing API, ~2x faster than KAPT and multiplatform-aware. KSP2 (stable since Kotlin 2.0) runs in the same compiler process. Used by Room, Dagger/Hilt, Moshi, kotlinx.serialization. (https://kotlinlang.org/docs/ksp-overview.html)
  • Compiler plugins: Native FIR-based compiler plugins (K2 era) — all-open, no-arg, kotlinx.serialization, parcelize, Compose. The Compose plugin rewrites @Composable functions to inject the Composer parameter and skip-key remember tables — visible in the K2 IR dump.
  • Contracts: kotlin.contracts (still experimental) lets stdlib functions describe their effects so the compiler can smart-cast across boundaries — e.g., requireNotNull(x) makes the compiler treat x as non-null after the call.
  • Inline + reified generics: inline fun <reified T> ... lets generic type info survive erasure at the call site. Combine with crossinline/noinline to control non-local returns.
  • Kotlin/Native memory model: Single-threaded mutability is no longer required (post-1.7.20). Workers use kotlinx.coroutines directly; the old freeze() API is deprecated.
  • KMP internals: expect/actual declarations resolve at link time; commonMain compiles to KLib (Kotlin’s portable IR format); platform sources compile to JVM bytecode, JS IR, or native LLVM bitcode.
  • kotlinx.serialization codegen: the compiler plugin generates a KSerializer<T> companion at compile time — inspectable via IR dump (-Xphases-to-dump-after=Generation).
  • R8/ProGuard interplay: Android R8 shrinks/optimizes Kotlin code; coroutines and reflection require explicit -keep rules. The Kotlin Gradle plugin ships consumer ProGuard rules in kotlin-stdlib.
  • Value classes: @JvmInline value class UserId(val raw: Long) — erased to the underlying type at the JVM call site, reducing allocation. Multi-field value classes are previewed in 2.x.
  • Context receivers / context parameters: Experimental in 1.6.20+, redesigned as context parameters (context(Logger)) targeted for stable in 2.x — gives “implicit dependency injection” without DI frameworks. Track KEEP-367.
  • Bytecode peek: Tools → Kotlin → Show Kotlin Bytecode in IntelliJ; the “Decompile” button reverses it back to equivalent Java.

Idioms & style

  • Naming: PascalCase for types/objects, camelCase for functions/properties, SCREAMING_SNAKE_CASE for const val. File names match the dominant top-level class, or use Foo.kt for utility file containing fun foo(...).
  • Formatter / linter: ktlint (Pinterest) for formatting + style enforcement; detekt for static analysis; IntelliJ’s built-in formatter follows the official conventions. The Kotlin Coding Conventions (https://kotlinlang.org/docs/coding-conventions.html) are the source of truth.
  • Idiomatic patterns: prefer val over var; use scope functions sparingly and intentionally (apply for builder-style mutation, let for nullable chaining, run for returning a value); use data class for DTOs; expose List<T> not MutableList<T> from public APIs; structured concurrency over fire-and-forget GlobalScope.
  • Expert review focus: misuse of !!, leaking MutableList, capturing the wrong receiver in scope functions, accidental Sequence re-iteration, blocking calls in Dispatchers.Default, swallowing CancellationException.

Ecosystem

  • Web/server: Ktor (JetBrains; coroutines-first), Spring Boot (Kotlin support is first-class), http4k, Javalin, Micronaut, Quarkus.
  • Android: Jetpack Compose (declarative UI), AndroidX, Hilt (Dagger codegen via KSP), Room, kotlinx.serialization.
  • Multiplatform: Compose Multiplatform (JetBrains; iOS/desktop/web/Android), KMP itself, SQLDelight, Ktor client, koin/kodein for DI.
  • Data/ML: Kotlin DataFrame, KotlinDL (deep learning), Multik (NumPy-equivalent).
  • Testing: JUnit 5, kotest (BDD-style + property testing), Spek, MockK (idiomatic mocking), Strikt (assertions), Turbine (Flow testing).
  • Docs: Dokka is the official KDoc-to-HTML/Markdown generator (replaces Javadoc).
  • Build: Gradle (canonical), Maven, Bazel, Amper (experimental). Compose Multiplatform packaging via compose.desktop.application.
  • Notable users: Google (Android, internal services), Netflix, Atlassian, Square/Cash App, JetBrains itself, Pinterest, Trello.

Gotchas

  • companion object is a real singleton, not Java’s static. @JvmStatic is required for true static methods callable from Java.
  • lateinit var is null at the bytecode level until set — accessing it pre-init throws UninitializedPropertyAccessException.
  • List<T> is read-only, not immutable — the underlying object can still be a MutableList cast away; kotlinx.collections.immutable provides true persistent collections.
  • MutableList<Foo> is invariant — you cannot pass MutableList<Subtype> where MutableList<Supertype> is expected.
  • Coroutines: catching Throwable will swallow CancellationException and break structured concurrency. Use coroutineContext.ensureActive() or rethrow.
  • == is equals (value); === is reference identity (opposite of Java).
  • Int? boxes to java.lang.Integer — beware autoboxing in hot loops.
  • KAPT is slow and being phased out; migrate to KSP. Some libraries (e.g., older Dagger) still require KAPT.
  • Kotlin/Native compile times are long (LLVM); use kotlin.native.cacheKind=static for incremental.
  • data class copy() is not deep — nested mutable refs are shared.
  • The inline keyword has three unrelated uses: inline fun (function inlining), inline class (deprecated, now value class), and inline val (compile-time constant property).

Citations