Bookmark this page. Use Ctrl+F (or Cmd+F on Mac) to find what you need. This cheat sheet covers Kotlin syntax from basics to coroutines. Try examples live at play.kotlinlang.org.

Last updated: April 2026

Variables

SyntaxDescription
val name = "Alex"Immutable (read-only) — cannot reassign
var count = 0Mutable — can reassign
val age: Int = 25Explicit type annotation
const val PI = 3.14Compile-time constant (top-level or object only)
val name = "Alex"       // type inferred as String
var score = 0           // type inferred as Int
score = 10              // OK — var can be reassigned
// name = "Sam"         // ERROR — val cannot be reassigned

Basic Types

TypeExampleNotes
Int4232-bit integer
Long42L64-bit integer
Double3.1464-bit decimal
Float3.14f32-bit decimal
Booleantrue, false
String"hello"Immutable
Char'A'Single character

Type Conversions

val x: Int = 42
val d: Double = x.toDouble()   // 42.0
val s: String = x.toString()   // "42"
val i: Int = "123".toInt()     // 123
val safe: Int? = "abc".toIntOrNull()  // null

Null Safety

SyntaxDescription
String?Nullable type — can hold null
name?.lengthSafe call — returns null if name is null
name ?: "default"Elvis operator — use default if null
name!!Not-null assertion — throws if null (avoid this)
name?.let { ... }Execute block only if not null
val name: String? = null
val len = name?.length          // null (no crash)
val safe = name ?: "Unknown"   // "Unknown"
// val crash = name!!.length   // throws NullPointerException

String Templates

val name = "Alex"
println("Hello $name")              // Hello Alex
println("Length: ${name.length}")   // Length: 4
println("Sum: ${2 + 3}")           // Sum: 5

Functions

// Regular function
fun greet(name: String): String {
    return "Hello $name"
}

// Single-expression function
fun greet(name: String) = "Hello $name"

// Default parameters
fun greet(name: String = "World") = "Hello $name"

// Named arguments
greet(name = "Alex")

// Unit return type (void equivalent)
fun log(message: String) {
    println(message)
}

Control Flow

if / else (is an expression)

val max = if (a > b) a else b

when (replaces switch)

when (x) {
    1 -> println("one")
    2, 3 -> println("two or three")
    in 4..10 -> println("between 4 and 10")
    is String -> println("it is a string")
    else -> println("something else")
}

// Guard conditions in when (Kotlin 2.2+)
when (val result = fetchData()) {
    is Result.Success if result.data.isNotEmpty() -> show(result.data)
    is Result.Success -> showEmpty()
    is Result.Error -> showError(result.message)
}

// when as expression
val label = when {
    score >= 90 -> "A"
    score >= 80 -> "B"
    else -> "C"
}

Loops

for (i in 1..5) { }          // 1, 2, 3, 4, 5
for (i in 1 until 5) { }     // 1, 2, 3, 4 (or 1..<5)
for (i in 1..<5) { }         // 1, 2, 3, 4 (open-ended range, Kotlin 1.8+)
for (i in 5 downTo 1) { }    // 5, 4, 3, 2, 1
for (i in 0..10 step 2) { }  // 0, 2, 4, 6, 8, 10
for (item in list) { }        // iterate a collection
list.forEachIndexed { index, item -> }

Collections

TypeCreateMutable Version
ListlistOf(1, 2, 3)mutableListOf(1, 2, 3)
SetsetOf(1, 2, 3)mutableSetOf(1, 2, 3)
MapmapOf("a" to 1)mutableMapOf("a" to 1)

Common Operations

val numbers = listOf(1, 2, 3, 4, 5)

numbers.filter { it > 2 }       // [3, 4, 5]
numbers.map { it * 2 }          // [2, 4, 6, 8, 10]
numbers.first()                  // 1
numbers.last()                   // 5
numbers.firstOrNull { it > 10 } // null
numbers.any { it > 3 }          // true
numbers.all { it > 0 }          // true
numbers.count { it % 2 == 0 }   // 2
numbers.sum()                    // 15
numbers.sorted()                 // [1, 2, 3, 4, 5]
numbers.reversed()               // [5, 4, 3, 2, 1]
numbers.distinct()               // remove duplicates
numbers.take(3)                  // [1, 2, 3]
numbers.drop(2)                  // [3, 4, 5]
numbers.groupBy { it % 2 }      // {1=[1,3,5], 0=[2,4]}
numbers.associate { it to it * it } // {1=1, 2=4, 3=9, ...}
numbers.flatMap { listOf(it, it * 10) } // [1,10,2,20,...]

Map Operations

val map = mapOf("a" to 1, "b" to 2)
map["a"]                    // 1
map.getOrDefault("c", 0)   // 0
map.keys                    // [a, b]
map.values                  // [1, 2]
map.entries                 // [a=1, b=2]
map + ("c" to 3)            // new map with c added

Lambdas

val double = { x: Int -> x * 2 }
double(5) // 10

// Single parameter uses "it"
val numbers = listOf(1, 2, 3)
numbers.filter { it > 1 }

// Trailing lambda
numbers.fold(0) { acc, n -> acc + n }

Classes

// Data class — equals, hashCode, toString, copy generated
data class User(val name: String, val age: Int)

val user = User("Alex", 25)
val copy = user.copy(age = 26)

// Enum class
enum class Color { RED, GREEN, BLUE }

// Sealed class — restricted hierarchy
sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val message: String) : Result()
    data object Loading : Result()
}

// Object — singleton
object Database {
    fun connect() { }
}

// Companion object — static-like members
class MyClass {
    companion object {
        fun create(): MyClass = MyClass()
    }
}

Extension Functions

fun String.addExclamation() = "$this!"

"Hello".addExclamation() // "Hello!"

fun List<Int>.secondOrNull(): Int? = if (size >= 2) this[1] else null

Scope Functions

FunctionObject refReturnUse case
letitLambda resultNull checks, transformations
runthisLambda resultObject config + compute result
withthisLambda resultGroup calls on an object
applythisObject itselfObject configuration
alsoitObject itselfSide effects (logging, validation)
// let — execute block if not null
val length = name?.let { it.length }

// apply — configure a builder/mutable object
val paint = Paint().apply {
    color = Color.RED
    strokeWidth = 4f
}

// also — side effects
val list = mutableListOf(1, 2).also { println("Before: $it") }

// run — compute a result
val result = service.run {
    connect()
    fetchData()
}

// with — group calls
val info = with(user) {
    "$name is $age years old"
}

Coroutines Basics

// Launch — fire and forget
scope.launch {
    val data = fetchData()  // suspend function
    updateUi(data)
}

// Async — returns a Deferred (future)
val deferred = async { fetchData() }
val result = deferred.await()

// Parallel execution
coroutineScope {
    val a = async { fetchA() }
    val b = async { fetchB() }
    println("${a.await()} ${b.await()}")
}

// Switch context
withContext(Dispatchers.IO) {
    // Run on background thread
    readFile()
}

// Flow — reactive stream
fun numbers(): Flow<Int> = flow {
    for (i in 1..3) {
        delay(100)
        emit(i)
    }
}

// collect is a suspend function — must be called inside a coroutine
scope.launch {
    numbers().collect { println(it) }
}
DispatcherUse case
Dispatchers.MainUI updates
Dispatchers.IONetwork, database, file I/O
Dispatchers.DefaultCPU-heavy computation

Smart Casts

// Kotlin auto-casts after a type check — no explicit cast needed
fun printLength(x: Any) {
    if (x is String) {
        println(x.length) // x is auto-cast to String
    }
}

// Works with when too
when (result) {
    is Result.Success -> println(result.data)
    is Result.Error -> println(result.message)
}

Value Classes

// Wraps a value with zero runtime overhead (no extra object allocation)
@JvmInline
value class Email(val address: String)

val email = Email("alex@example.com")
// At runtime, this is just a String — no wrapper object

Multi-line Strings

val json = """
    {
        "name": "Alex",
        "age": 25
    }
""".trimIndent()

val sql = """
    SELECT * FROM users
    WHERE age > 18
    ORDER BY name
""".trimIndent()

Common Patterns

Safe casting

val x: Any = "hello"
val s: String? = x as? String   // "hello"
val i: Int? = x as? Int         // null (no crash)

Destructuring

val (name, age) = User("Alex", 25)
val (key, value) = mapEntry

takeIf / takeUnless

val positiveNumber = number.takeIf { it > 0 }   // number or null
val nonBlank = name.takeUnless { it.isBlank() }  // name or null

// Chain with Elvis
val port = config.getPort().takeIf { it in 1..65535 } ?: 8080

Lazy initialization

val heavy: String by lazy {
    println("Computed!")
    "result"
}

Common Mistakes

  1. Mutable vs immutable collectionslistOf() returns a read-only List. Use mutableListOf() if you need add() or remove(). Casting a List to MutableList is unsafe.

  2. == vs ===== checks structural equality (like Java’s .equals()). === checks referential equality (same object in memory). Most of the time you want ==.

  3. Nullable type confusionString and String? are different types. A non-null String cannot hold null. If a function might return null, the return type must be String?.