Kotlin Cheatsheet

Null safety, coroutines, data classes, sealed classes, collections, generics & Android basics

Language
Contents

Basics & Types

fun main() {
    println("Hello Kotlin!")
}

// Variables
val name = "Alice"       // immutable (prefer val)
var age = 30             // mutable
val pi: Double = 3.14

// Types: Int, Long, Double, Float, Boolean, Char, String, Byte, Short
val x: Int = 42
val big: Long = 100_000_000L
val f: Float = 3.14f
val flag: Boolean = true

// String templates
val s: String = "Hello ${name}, age $age"
val multiline = """
    |Multi-line
    |string
""".trimMargin()

// Null safety
var nullable: String? = null
nullable?.length              // safe call → null
nullable?.length ?: 0         // Elvis operator → 0
nullable!!.length             // assert non-null (throws if null)

// Type checks & casts
if (obj is String) obj.length  // smart cast
val str = obj as? String       // safe cast (null if fails)
val str2 = obj as String       // unsafe cast (throws ClassCastException)

// Constants
const val MAX = 100          // compile-time constant (top-level or companion)

// Type aliases
typealias UserMap = Map<String, List<Int>>

// Ranges
val r1 = 1..10         // 1 to 10 inclusive
val r2 = 1 until 10    // 1 to 9 (exclusive end)
val r3 = 10 downTo 1   // descending
val r4 = 1..10 step 2  // 1, 3, 5, 7, 9
🔀

Control Flow

// if 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("4..10")
    !in 0..20  -> println("out of range")
    is String  -> println("it's a string")
    else       -> println("other")
}

// when as expression (must be exhaustive)
val label = when {
    score >= 90 -> "A"
    score >= 80 -> "B"
    else        -> "C"
}

// for loops
for (i in 1..5) println(i)
for (i in 5 downTo 1 step 2) println(i)
for ((index, value) in list.withIndex()) println("$index: $value")
for ((key, value) in map) println("$key=$value")

// while / do-while
while (x > 0) { x-- }
do { x++ } while (x < 10)

// Labels & break/continue
outer@ for (i in 1..5) {
    for (j in 1..5) {
        if (j == 3) break@outer
    }
}

// repeat
repeat(5) { i -> println("Iteration $i") }
🔧

Functions & Lambdas

// Functions
fun add(a: Int, b: Int): Int = a + b  // expression body
fun greet(name: String = "World") = println("Hello $name")
greet()                 // "Hello World"
greet(name = "Alice")   // named argument

// vararg
fun sum(vararg nums: Int): Int = nums.sum()
sum(1, 2, 3)
sum(*intArrayOf(1,2,3))  // spread operator

// Infix functions
infix fun Int.times(str: String) = str.repeat(this)
3 times "Hi "  // "Hi Hi Hi "

// Lambdas
val double = { x: Int -> x * 2 }
val nums = listOf(1,2,3)
nums.filter { it % 2 == 0 }    // implicit 'it'
nums.map { it * 2 }
nums.reduce { acc, n -> acc + n }

// Higher-order functions
fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int = op(a, b)
operate(3, 4) { x, y -> x + y }  // trailing lambda

// Function references
val ref = ::add              // top-level function reference
val ref2 = String::length    // member reference
listOf("a","bb").map(String::length)

// Inline functions (avoid lambda allocation overhead)
inline fun <reified T> isType(value: Any): Boolean = value is T

// Extension functions
fun String.addExclaim() = "$this!"
"Hello".addExclaim()  // "Hello!"

// Scope functions
person.let { println(it.name) }                  // let: 'it' ref, returns result
person.apply { name = "Bob"; age = 25 }          // apply: 'this' ref, returns receiver
person.also { log(it) }                          // also: 'it' ref, returns receiver
person.run { "$name is $age" }                   // run: 'this' ref, returns result
with(person) { println("$name $age") }           // with: 'this' ref, returns result
📦

Collections

// Immutable (read-only) collections
val list = listOf(1, 2, 3)
val set = setOf("a", "b")
val map = mapOf("x" to 1, "y" to 2)

// Mutable collections
val mList = mutableListOf(1, 2)
mList.add(3);  mList.removeAt(0);  mList += 4
val mSet = mutableSetOf("a")
val mMap = mutableMapOf("x" to 1)
mMap["y"] = 2

// Array
val arr = arrayOf(1, 2, 3)
val intArr = intArrayOf(1, 2, 3)
val sized = Array(5) { i -> i * 2 }  // [0,2,4,6,8]

// Collection operations
list.filter { it > 1 }               // [2, 3]
list.map { it * 10 }                 // [10, 20, 30]
list.flatMap { listOf(it, it + 1) } // [1,2,2,3,3,4]
list.find { it > 1 }                 // 2 (first match or null)
list.any { it > 2 }                  // true
list.all { it > 0 }                  // true
list.none { it > 5 }                 // true
list.count { it > 1 }                // 2
list.groupBy { it % 2 }              // {1=[1,3], 0=[2]}
list.associate { it to it * 10 }     // {1=10, 2=20, 3=30}
list.partition { it > 1 }            // ([2,3], [1])
list.zip(listOf("a","b","c"))      // [(1,a), (2,b), (3,c)]
list.chunked(2)                      // [[1,2], [3]]
list.windowed(2)                     // [[1,2], [2,3]]
list.fold(0) { acc, n -> acc + n }   // 6
list.reduce { acc, n -> acc + n }    // 6
list.sorted()                        // natural order
list.sortedByDescending { it }
list.take(2);  list.drop(1)
list.distinct();  list.reversed()

// Map operations
map.keys;  map.values;  map.entries
map.getOrDefault("z", 0)
map.filter { (k, v) -> v > 1 }
map.mapValues { (_, v) -> v * 10 }
map.mapKeys { (k, _) -> k.uppercase() }

// Sequences (lazy evaluation)
list.asSequence()
    .filter { it > 1 }
    .map { it * 10 }
    .toList()

generateSequence(1) { it * 2 }.take(5).toList()  // [1,2,4,8,16]
🏛️

Classes & OOP

// Basic class
class Person(val name: String, var age: Int) {
    init { println("Created $name") }     // initializer block
    constructor(name: String) : this(name, 0)  // secondary constructor
}

// Data class (auto equals, hashCode, toString, copy, componentN)
data class User(val name: String, val age: Int)
val user = User("Alice", 30)
val copy = user.copy(age = 31)
val (name, age) = user     // destructuring

// Sealed class (restricted hierarchy)
sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val msg: String) : Result()
    object Loading : Result()
}
when (result) {
    is Result.Success -> println(result.data)
    is Result.Error   -> println(result.msg)
    Result.Loading    -> println("Loading...")
}

// Sealed interface (Kotlin 1.5+)
sealed interface Shape
data class Circle(val r: Double) : Shape
data class Rect(val w: Double, val h: Double) : Shape

// Enum class
enum class Direction(val dx: Int, val dy: Int) {
    UP(0,1), DOWN(0,-1), LEFT(-1,0), RIGHT(1,0);
    fun opposite() = when(this) {
        UP -> DOWN;  DOWN -> UP;  LEFT -> RIGHT;  RIGHT -> LEFT
    }
}

// Value class (inline wrapper, zero overhead)
@JvmInline
value class Email(val value: String)

// Object (singleton) & companion
object Database { fun connect() {} }
class MyClass {
    companion object {
        fun create() = MyClass()  // like static
    }
}

// Inheritance
open class Animal(val name: String) {
    open fun sound() = "..."
}
class Dog(name: String) : Animal(name) {
    override fun sound() = "Woof!"
}

// Interface
interface Drawable {
    fun draw()
    fun description() = "Drawable object"  // default implementation
}

// Abstract class
abstract class Vehicle {
    abstract val speed: Int
    fun info() = "Speed: $speed"
}

// Delegation
class CountingSet<T>(
    val inner: MutableSet<T> = mutableSetOf()
) : MutableSet<T> by inner {
    var addCount = 0
    override fun add(elem: T): Boolean { addCount++; return inner.add(elem) }
}
🔷

Generics

// Generic class
class Box<T>(val value: T)
val box = Box("hello")  // Box<String> inferred

// Generic function
fun <T> singletonList(item: T): List<T> = listOf(item)

// Bounded generics
fun <T : Comparable<T>> sort(list: List<T>) { }

// Multiple bounds
fun <T> copy(item: T) where T : Serializable, T : Comparable<T> { }

// Variance
class Producer<out T>(val value: T)    // covariant (out = producer)
class Consumer<in T> { fun consume(item: T) {} }  // contravariant

// Star projection (like Java's ?)
fun printAll(list: List<*>) { list.forEach { println(it) } }

// Reified type parameters (inline only)
inline fun <reified T> isA(value: Any) = value is T
isA<String>("hi")  // true
🌀

Coroutines & Flow

import kotlinx.coroutines.*

// Launch (fire and forget)
fun main() = runBlocking {
    launch {
        delay(1000)
        println("World!")
    }
    println("Hello")
}

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

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

// Dispatchers
launch(Dispatchers.IO) { }       // I/O operations (network, disk)
launch(Dispatchers.Default) { }  // CPU-intensive work
launch(Dispatchers.Main) { }     // UI thread (Android)
launch(Dispatchers.Unconfined) { } // starts in caller thread

// withContext (switch dispatcher)
suspend fun fetchData(): String = withContext(Dispatchers.IO) {
    // network call here
    "data"
}

// Structured concurrency & cancellation
val job = launch {
    repeat(1000) { i ->
        println("job: $i")
        delay(500)         // cancellable suspension point
    }
}
delay(1300)
job.cancelAndJoin()  // cancel and wait for completion

// Timeout
withTimeout(3000) { /* throws TimeoutCancellationException */ }
withTimeoutOrNull(3000) { /* returns null on timeout */ }

// Flow (cold reactive stream)
fun numbers(): Flow<Int> = flow {
    for (i in 1..5) {
        delay(100)
        emit(i)
    }
}
numbers()
    .filter { it % 2 == 0 }
    .map { it * 10 }
    .collect { println(it) }

// StateFlow & SharedFlow
val state = MutableStateFlow(0)    // hot, holds latest value
state.value = 42
state.collect { println(it) }

val shared = MutableSharedFlow<String>()  // hot, no initial value
shared.emit("event")

// Flow operators
flow.onEach { log(it) }
flow.catch { e -> emit(fallback) }
flow.onCompletion { cause -> /* cleanup */ }
flow.debounce(300)
flow.distinctUntilChanged()
flow.combine(otherFlow) { a, b -> a + b }
flow.flatMapLatest { fetchDetails(it) }
🛡️

Error Handling

// try/catch/finally
try {
    val result = riskyOperation()
} catch (e: IOException) {
    println("IO error: ${e.message}")
} catch (e: Exception) {
    println("Error: ${e.message}")
} finally {
    cleanup()
}

// try is an expression
val number = try { str.toInt() } catch (e: NumberFormatException) { 0 }

// throw is an expression (return type: Nothing)
val name = person.name ?: throw IllegalArgumentException("Name required")

// Custom exceptions
class ApiException(message: String, val code: Int) : Exception(message)

// require / check / error (preconditions)
require(age >= 0) { "Age must be non-negative" }  // IllegalArgumentException
check(isInitialized) { "Not initialized" }           // IllegalStateException
error("Something went wrong")                         // IllegalStateException

// Result type (functional error handling)
val result = runCatching { riskyCall() }
result.isSuccess
result.isFailure
result.getOrNull()
result.getOrDefault("fallback")
result.getOrElse { e -> "Error: ${e.message}" }
result.onSuccess { data -> process(data) }
       .onFailure { e -> log(e) }
result.map { it.uppercase() }
result.recover { e -> "recovered" }
🧩

DSLs & Delegates

// Property delegates
val lazyVal by lazy { expensiveCompute() }  // computed once on first access

var observed by Delegates.observable("initial") { _, old, new ->
    println("Changed from $old to $new")
}

var validated by Delegates.vetoable(0) { _, _, new ->
    new >= 0  // reject negative values
}

// Map delegation
class Config(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int by map
}
val config = Config(mapOf("name" to "Alice", "age" to 30))

// Type-safe builders (DSL)
fun html(block: HTML.() -> Unit): HTML = HTML().apply(block)

html {
    head { title("Page") }
    body {
        p("Hello World")
        ul {
            listOf("a","b","c").forEach { li(it) }
        }
    }
}

// @DslMarker (restricts scope leaking)
@DslMarker
annotation class HtmlDsl

// Operator overloading
data class Point(val x: Int, val y: Int) {
    operator fun plus(other: Point) = Point(x + other.x, y + other.y)
    operator fun times(scale: Int) = Point(x * scale, y * scale)
}
Point(1,2) + Point(3,4)  // Point(4, 6)
📱

Android Development

// ── Jetpack Compose ──

// Composable function
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(text = "Hello $name!", modifier = modifier)
}

// State management
@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Count: $count")
    }
}

// Common Compose layouts
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
    Text("Title", style = MaterialTheme.typography.headlineMedium)
    Spacer(modifier = Modifier.height(8.dp))
    Row(horizontalArrangement = Arrangement.SpaceBetween) {
        Icon(Icons.Default.Home, contentDescription = "Home")
        Text("Home")
    }
    LazyColumn {
        items(users) { user -> UserCard(user) }
    }
}

// Navigation (Compose)
val navController = rememberNavController()
NavHost(navController, startDestination = "home") {
    composable("home") { HomeScreen(navController) }
    composable("detail/{id}") { backStackEntry ->
        DetailScreen(backStackEntry.arguments?.getString("id"))
    }
}
navController.navigate("detail/42")

// ── ViewModel ──
class MainViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(UiState())
    val uiState = _uiState.asStateFlow()

    fun loadData() {
        viewModelScope.launch {
            _uiState.value = UiState(loading = true)
            val data = repository.fetchData()
            _uiState.value = UiState(data = data)
        }
    }
}

// Collecting state in Compose
@Composable
fun Screen(viewModel: MainViewModel = viewModel()) {
    val uiState by viewModel.uiState.collectAsState()
    when {
        uiState.loading -> CircularProgressIndicator()
        uiState.error != null -> Text("Error: ${uiState.error}")
        else -> DataList(uiState.data)
    }
}

// ── Room Database ──
@Entity("users")
data class UserEntity(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    val name: String,
    val email: String
)

@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAll(): Flow<List<UserEntity>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(user: UserEntity)

    @Delete
    suspend fun delete(user: UserEntity)
}

// ── Retrofit (Networking) ──
interface ApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: Int): User

    @POST("users")
    suspend fun createUser(@Body user: User): Response<User>
}

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()

// ── Hilt Dependency Injection ──
@HiltAndroidApp
class MyApp : Application()

@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Provides @Singleton
    fun provideApi(): ApiService = retrofit.create(ApiService::class.java)
}

@HiltViewModel
class MainViewModel @Inject constructor(
    private val api: ApiService
) : ViewModel() { }
🌐

Kotlin Multiplatform & Web

// ── Kotlin Multiplatform (KMP) ──
// Share code between Android, iOS, Desktop, Web

// Shared module — expect/actual pattern
// commonMain/
expect fun platformName(): String
fun greet() = "Hello from ${platformName()}"

// androidMain/
actual fun platformName() = "Android"

// iosMain/
actual fun platformName() = "iOS"

// Shared ViewModel with KMP
class SharedViewModel {
    private val _state = MutableStateFlow(UiState())
    val state = _state.asStateFlow()

    fun loadItems() {
        coroutineScope.launch {
            val items = repository.getItems()
            _state.value = UiState(items = items)
        }
    }
}

// ── Ktor (Server-Side Kotlin) ──
fun main() {
    embeddedServer(Netty, port = 8080) {
        install(ContentNegotiation) { json() }
        install(StatusPages) {
            exception<Throwable> { call, cause ->
                call.respondText(cause.message ?: "Error",
                    status = HttpStatusCode.InternalServerError)
            }
        }
        routing {
            get("/") { call.respondText("Hello, Ktor!") }
            get("/users/{id}") {
                val id = call.parameters["id"]?.toIntOrNull()
                val user = userService.findById(id!!)
                call.respond(user)
            }
            post("/users") {
                val user = call.receive<User>()
                userService.create(user)
                call.respond(HttpStatusCode.Created, user)
            }
        }
    }.start(wait = true)
}

// Ktor Client (shared across platforms)
val client = HttpClient {
    install(ContentNegotiation) { json() }
    install(Logging) { level = LogLevel.INFO }
}
val response: User = client.get("https://api.example.com/users/1").body()

// ── Kotlin/JS ──
fun main() {
    document.getElementById("root")?.innerHTML = "Hello from Kotlin/JS"
}

// Kotlin/JS interop with JavaScript
external fun alert(message: String)
external interface JsUser {
    val name: String
    val age: Int
}

// ── Compose Multiplatform ──
// UI framework for Android, iOS, Desktop, Web
@Composable
fun App() {
    MaterialTheme {
        var text by remember { mutableStateOf("Hello, World!") }
        Button(onClick = { text = "Clicked!" }) {
            Text(text)
        }
    }
}

// ── Kotlinx Serialization ──
@Serializable
data class User(val name: String, val age: Int)
val json = Json.encodeToString(User("Alice", 30))
val user = Json.decodeFromString<User>(json)

// ── Gradle Kotlin DSL (build.gradle.kts) ──
plugins {
    kotlin("multiplatform") version "2.0.0"
    id("org.jetbrains.compose")
}
kotlin {
    androidTarget()
    iosArm64()
    js(IR) { browser() }
    sourceSets {
        commonMain.dependencies {
            implementation(compose.runtime)
            implementation(compose.material3)
            implementation("io.ktor:ktor-client-core:2.3.0")
        }
    }
}