thecodex.expert · The Codex Family of Knowledge
Language Reference

Kotlin

Modern JVM language — null-safe, concise, fully Java-interoperable, and Google's preferred language for Android development.

Language Reference Kotlin 2.4 · 2026 JVM + Multiplatform Last verified:
New to Kotlin? This is the reference encyclopedia — dip in anytime. To learn step by step, start the Kotlin Course →
Canonical Definition

Kotlin is a statically typed, multi-platform programming language developed by JetBrains that compiles to JVM bytecode, JavaScript, and native code — offering null safety, concise syntax, 100% Java interoperability, and coroutines for asynchronous programming, and is Google’s preferred language for Android development.

📑 Kotlin Reference — All Topics

Null Safety & Types

?, !!, let, also, run — Kotlin's null system.

Idioms & Stdlib

Data classes, extension functions, apply, scope functions.

Coroutines & Flow

suspend, launch, async, Flow for reactive streams.

What is Kotlin

JetBrains built it to fix Java's pain points — then Google made it Android's language.

Setup and Tooling

kotlinc compiles a single file — but the moment there's a dependency, it's Gradle.

Variables and Types

val is the default nearly everyone reaches for — var only when reassignment is real.

Control Flow

No ternary operator exists at all — because if already works as an expression.

Functions

Default parameters replace most of what Java ever needed method overloading for.

Classes and Objects

Every class is final by default — the exact opposite of Java's design.

Data Classes

One keyword generates equals, hashCode, toString, copy, and destructuring at once.

Sealed Classes

Add a new subclass, and every when missing that case is a compile error.

Extension Functions

Add a method to String itself — no inheritance, no touching the original source.

Lambdas and Higher-Order Functions

list.filter { it > 5 } is just a trailing lambda moved outside the parens.

Collections

List isn't a deep guarantee — it's a read-only VIEW of possibly-mutable data.

Generics

out Animal is declared once on the class — not repeated at every call site.

Interfaces

Default method bodies since Kotlin's very first release — Java needed until 8.

Object Declarations and Companions

No static keyword exists at all — companion object plays that role instead.

Delegation

The by keyword forwards an entire interface automatically — composition, no boilerplate.

Scope Functions

apply and also do almost the same thing — the whole difference is this vs it.

Kotlin Multiplatform

[Wireframe — content coming soon.]

Jetpack Compose Basics

[Wireframe — content coming soon.]

Testing

kotlin.test isn't its own framework — it runs on JUnit underneath, on the JVM.

Exception Handling

No checked exceptions exist — a deliberate rejection of Java's design.

Kotlin and Java Interop

Anything from Java is a platform type — the one real hole in null safety.

Annotations

One property compiles to a field, a getter, AND a param — pick which one exactly.

Kotlin DSL

[Wireframe — content coming soon.]

Standard Library Deep Dive

[Wireframe — content coming soon.]

Build Tools

[Wireframe — content coming soon.]

Kotlin/Native

[Wireframe — content coming soon.]

Coroutines Deep Dive

A parent scope genuinely cannot finish until every child has completed.

One sentence

Kotlin is what Java should have been — it runs on the JVM, is 100% interoperable with Java, but removes the verbosity and null-pointer crashes that made Java painful, and adds modern features like extension functions, data classes, and coroutines.

What Kotlin is

Kotlin is a statically typed, multi-platform programming language developed by JetBrains, with version 1.0 released in February 2016. Kotlin compiles to JVM bytecode (interoperable with all Java libraries), JavaScript, and native machine code (Kotlin/Native). In May 2017, Google announced Kotlin as an officially supported language for Android development, and since 2019 it is the preferred language for Android. Kotlin's design goals: 100% Java interoperability, null safety built into the type system, conciseness (typically 40% less code than equivalent Java), and expressiveness.

Kotlinhello.kt
fun main() {
    // Type inference — no need to declare types explicitly
    val name = "Priya"          // val = immutable (like Java final)
    var count = 0               // var = mutable
    val pi = 3.14159

    println("Hello, $name!")    // string templates — no concatenation

    // Null safety built in — types are non-null by default
    val city: String = "Mumbai"   // cannot be null
    val nickname: String? = null  // ? = nullable type

    // Safe call operator
    println(nickname?.length)     // null — no NullPointerException
    println(nickname?.length ?: "no nickname")  // Elvis: fallback value

    // When expression (enhanced switch)
    val result = when (count) {
        0    -> "zero"
        1, 2 -> "small"
        else -> "large"
    }
    println(result)
}

Null safety — eliminating NullPointerException

Null safety is Kotlin's defining feature. Every type is non-null by default — String can never be null. To allow null, append ?: String?. The compiler enforces safe access: you cannot call methods on a nullable type without handling the null case first. This eliminates the NullPointerException — the "billion dollar mistake" — at compile time.

Kotlinnull_safety.kt
fun findUser(id: Int): String? {
    return if (id == 1) "Priya" else null
}

fun main() {
    val user = findUser(99)

    // Safe call — returns null instead of throwing
    println(user?.length)            // null

    // Elvis operator — provide a default
    val name = user ?: "Anonymous"   // "Anonymous"

    // Non-null assertion — throws KotlinNullPointerException if null
    // Only use when you are CERTAIN it is not null
    // val len = user!!.length       // dangerous

    // Smart cast — after null check, compiler knows type
    if (user != null) {
        println(user.length)   // OK — compiler narrows to String
    }

    // let — run block only if non-null
    user?.let { u ->
        println("Found: $u (${u.length} chars)")
    }
}

Data classes

A data class automatically generates equals(), hashCode(), toString(), copy(), and component functions for destructuring. This replaces hundreds of lines of Java boilerplate for plain data-holding classes.

Kotlindata_classes.kt
// One line replaces ~50 lines of Java
data class User(
    val id: Int,
    val name: String,
    val email: String,
    val active: Boolean = true  // default parameter
)

fun main() {
    val user1 = User(1, "Priya", "priya@example.com")
    val user2 = user1.copy(email = "new@example.com")  // changed copy

    println(user1)                    // User(id=1, name=Priya, email=priya@example.com, active=true)
    println(user1 == user2)           // false — structural equality
    println(user1 === user2)          // false — reference equality

    // Destructuring
    val (id, name, email) = user1
    println("$id: $name")
}

Extension functions and properties

Extension functions let you add methods to existing classes without inheriting from them or modifying their source code. They are resolved statically (at compile time), not dynamically — they do not actually modify the class.

Kotlinextensions.kt
// Add a method to String without touching the String class
fun String.isPalindrome(): Boolean {
    return this == this.reversed()
}

// Extension property
val String.wordCount: Int
    get() = if (isBlank()) 0 else trim().split("\s+".toRegex()).size

fun main() {
    println("racecar".isPalindrome())      // true
    println("hello world".wordCount)       // 2
    println("  hello   world  ".wordCount) // 2

    // Standard library uses extensions extensively
    val nums = listOf(1, 2, 3, 4, 5)
    println(nums.filter { it % 2 == 0 })   // [2, 4]
    println(nums.map { it * it })           // [1, 4, 9, 16, 25]
    println(nums.sumOf { it })              // 15
}

Collections and functional operations

Kotlin's collection API is built on extension functions. Collections are immutable by default (listOf, mapOf, setOf). Mutable variants: mutableListOf, mutableMapOf. Kotlin provides a rich set of higher-order functions: map, filter, reduce, fold, flatMap, groupBy, sortedBy, distinct, zip, partition.

Sealed classes and exhaustive when

A sealed class restricts which classes can inherit from it — all subclasses must be in the same package/file. Combined with when, this creates exhaustive pattern matching: the compiler verifies every subclass is handled, eliminating the need for an else branch and catching missing cases at compile time. This is Kotlin's equivalent of Rust's enums or Haskell's algebraic data types.

Kotlinsealed_classes.kt
sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val message: String, val code: Int) : Result<Nothing>()
    object Loading : Result<Nothing>()
}

fun handleResult(result: Result<String>) {
    // Compiler verifies all cases are handled — no else needed
    when (result) {
        is Result.Success -> println("Got: ${result.data}")
        is Result.Error   -> println("Error ${result.code}: ${result.message}")
        Result.Loading    -> println("Loading...")
    }
}

fun main() {
    handleResult(Result.Success("Hello"))
    handleResult(Result.Error("Not found", 404))
    handleResult(Result.Loading)
}

Coroutines — structured concurrency

Kotlin coroutines are the standard way to write asynchronous code in Kotlin. A coroutine is a suspendable computation — it can be suspended (paused) without blocking a thread, and resumed later. Coroutines are not threads: millions of coroutines can coexist on a handful of threads. The suspend keyword marks a function that can be suspended. Coroutines use structured concurrency: a coroutine is always launched in a CoroutineScope, and if the scope is cancelled, all child coroutines are cancelled too — preventing coroutine leaks.

Kotlincoroutines.kt
import kotlinx.coroutines.*

suspend fun fetchUser(id: Int): String {
    delay(100)   // non-blocking delay — suspends coroutine, not thread
    return "User$id"
}

fun main() = runBlocking {   // creates coroutine scope for main
    // Launch concurrent coroutines
    val job1 = async { fetchUser(1) }
    val job2 = async { fetchUser(2) }

    // Await both results — runs in parallel
    println("${job1.await()} and ${job2.await()}")

    // Structured: launch N coroutines and wait for all
    val results = (1..5).map { id ->
        async { fetchUser(id) }
    }.awaitAll()
    println(results)

    // Flow — cold asynchronous stream (like Rx)
    // flow { emit(1); emit(2); emit(3) }
    //     .filter { it > 1 }
    //     .collect { println(it) }
}

Kotlin vs Java: key differences

FeatureJavaKotlin
Null safetyNo — NPE at runtimeYes — compiler enforces non-null by default
Data classes~50 lines of boilerplatedata class User(val name: String)
Extension functionsNoYes — add methods to any class
Default parametersNo — method overloadingYes — fun f(x: Int = 0)
Smart castExplicit cast needed after instanceofAuto-cast after is-check
String templates"Hello " + name"Hello $name"
Functional collectionsJava Streams APIBuilt-in extension functions
CoroutinesNo (Project Loom is similar)First-class via kotlinx.coroutines
Immutabilityfinal keywordval (immutable) vs var (mutable)

Kotlin Multiplatform

Kotlin Multiplatform (KMP) allows sharing business logic code across platforms — Android, iOS, web (via Kotlin/JS), desktop, and server — while using native UI frameworks on each platform. KMP compiles Kotlin to JVM bytecode (Android/JVM), JavaScript (web), and LLVM-based native code (iOS, macOS, Linux). This is distinct from Flutter/React Native which render UI uniformly; KMP shares only logic, not UI. JetBrains declared KMP stable in November 2023.

Commonly confused
Kotlin is not a Java replacement — it is a Java companion. Kotlin files and Java files can coexist in the same project. You can call any Java library from Kotlin and any Kotlin class from Java. Android projects routinely mix both. You do not need to rewrite Java code to use Kotlin.
val does not mean the object is immutable — it means the reference cannot be reassigned. val list = mutableListOf(1,2,3): list is val, but you can still call list.add(4). The list reference is fixed; the list contents are not. For a truly immutable list, use listOf().
Kotlin coroutines are not threads. launch { } creates a coroutine, not a thread. Thousands of coroutines can run on a single thread by suspending and resuming. Coroutines use thread pools (Dispatchers.IO, Dispatchers.Default) managed by the runtime — you do not create OS threads manually.

Kotlin's type system: nullability and generics

Kotlin's type system distinguishes nullable (T?) and non-nullable (T) types at the type level. The compiler tracks nullability through the control flow graph — after a null check, the type is narrowed (smart cast). Kotlin's generics are similar to Java's but add explicit variance annotations: out T (covariant — producer), in T (contravariant — consumer). This resolves the use-site variance verbosity of Java (? extends T, ? super T) by declaring variance at the declaration site. Nothing is Kotlin's bottom type — a value of type Nothing never exists; functions that always throw or loop forever return Nothing.

Coroutines implementation: continuation-passing style

Kotlin coroutines are implemented via continuation-passing style (CPS) transformation at compile time. A suspend fun is transformed by the compiler into a state machine with a hidden Continuation<T> parameter. Each suspension point becomes a state in the machine. When suspended, the current state is saved in the Continuation object (heap-allocated) and the thread is released. When resumed, the state machine continues from where it left off. This is why millions of coroutines can exist with minimal overhead — each coroutine stores only its current state, not an entire OS thread stack.

Inline functions and reified generics

Kotlin inline functions have their body inlined at call sites — eliminating the overhead of lambda object creation and function call. This is critical for higher-order functions used in hot loops: filter, map, forEach are all inline. reified type parameters (only available with inline) allow accessing the actual runtime type of a generic parameter — solving the type erasure problem. inline fun <reified T> instanceOf(value: Any) = value is T works because the compiler inlines the function and substitutes the actual type for T.

Specification reference

JetBrains. Kotlin Language Specification. kotlinlang.org/spec/. Kotlin documentation: kotlinlang.org/docs/. Kotlin coroutines guide: kotlinlang.org/docs/coroutines-guide.html. JetBrains. "Kotlin Multiplatform Stable." blog.jetbrains.com, November 2023.

Sources

1
JetBrains. Kotlin Language Specification. kotlinlang.org/spec/.
2
JetBrains. Kotlin documentation. kotlinlang.org/docs/. — Authoritative Kotlin reference.
3
JetBrains. Kotlin coroutines guide. kotlinlang.org/docs/coroutines-guide.html.
4
Jemerov, D. & Isakova, S. (2017). Kotlin in Action. Manning Publications.
5
JetBrains. "Kotlin Multiplatform Stable." blog.jetbrains.com, November 2023.
Source confidence: High Last verified: Primary source: Kotlin documentation — kotlinlang.org/docs/