Kotlin Advanced Comprehensive Cheat Sheet

Last Updated: December 2025

A complete guide to advanced Kotlin development, covering coroutines and structured concurrency, Flow for reactive programming, Kotlin Multiplatform (KMP), DSL builders, advanced language features, Android development patterns with Jetpack Compose, testing strategies, and production best practices.

Advanced Language Features

Extension Functions & Properties

// ===== EXTENSION FUNCTIONS =====

fun String.isValidEmail(): Boolean {
    val emailRegex = Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")
    return emailRegex.matches(this)
}

fun  List.second(): T {
    if (size < 2) throw NoSuchElementException("List has less than 2 elements")
    return this[1]
}

fun Int.times(action: (Int) -> Unit) {
    repeat(this) { action(it) }
}

// Usage
val email = "[email protected]"
println(email.isValidEmail())  // true

val list = listOf(1, 2, 3, 4)
println(list.second())  // 2

5.times { println("Iteration $it") }


// ===== EXTENSION PROPERTIES =====

val String.firstChar: Char
    get() = this[0]

val List.sum: Int
    get() = this.sum()

// With backing field (not allowed - must use function)
// Extensions can't have state

val  List.lastIndex: Int
    get() = size - 1


// ===== NULLABLE RECEIVER =====

fun String?.orEmpty(): String = this ?: ""

fun Any?.toStringOrEmpty(): String = this?.toString() ?: ""

// Usage
val nullString: String? = null
println(nullString.orEmpty())  // ""


// ===== COMPANION OBJECT EXTENSIONS =====

class User(val name: String) {
    companion object
}

fun User.Companion.create(name: String): User {
    return User(name)
}

// Usage
val user = User.create("Alice")


// ===== INFIX FUNCTIONS =====

infix fun Int.pow(exponent: Int): Int {
    return Math.pow(this.toDouble(), exponent.toDouble()).toInt()
}

infix fun String.shouldBe(expected: String) {
    if (this != expected) throw AssertionError("Expected $expected but got $this")
}

// Usage
val result = 2 pow 3  // 8
"hello" shouldBe "hello"


// ===== OPERATOR OVERLOADING =====

data class Vector(val x: Int, val y: Int) {
    operator fun plus(other: Vector) = Vector(x + other.x, y + other.y)
    operator fun minus(other: Vector) = Vector(x - other.x, y - other.y)
    operator fun times(scalar: Int) = Vector(x * scalar, y * scalar)
    operator fun unaryMinus() = Vector(-x, -y)
    operator fun get(index: Int) = when (index) {
        0 -> x
        1 -> y
        else -> throw IndexOutOfBoundsException()
    }
}

// Usage
val v1 = Vector(1, 2)
val v2 = Vector(3, 4)
val v3 = v1 + v2       // Vector(4, 6)
val v4 = v1 * 3        // Vector(3, 6)
val v5 = -v1           // Vector(-1, -2)
println(v1[0])         // 1


// ===== INVOKE OPERATOR =====

class Greeter(val greeting: String) {
    operator fun invoke(name: String) = "$greeting, $name!"
}

// Usage
val greeter = Greeter("Hello")
println(greeter("Alice"))  // "Hello, Alice!"


// ===== DESTRUCTURING =====

data class User(val id: Int, val name: String, val email: String)

val user = User(1, "Alice", "[email protected]")
val (id, name, email) = user

// Custom destructuring
class Result(val success: Boolean, val data: String) {
    operator fun component1() = success
    operator fun component2() = data
}

val (success, data) = Result(true, "Done")


// ===== SEALED CLASSES & WHEN EXHAUSTIVE =====

sealed class Result {
    data class Success(val data: T) : Result()
    data class Error(val message: String) : Result()
    data object Loading : Result()
}

fun  handleResult(result: Result) = when (result) {
    is Result.Success -> println("Data: ${result.data}")
    is Result.Error -> println("Error: ${result.message}")
    Result.Loading -> println("Loading...")
    // No else needed - exhaustive!
}


// ===== INLINE CLASSES (Value Classes) =====

@JvmInline
value class UserId(val value: Int)

@JvmInline
value class Email(val value: String) {
    init {
        require(value.contains("@")) { "Invalid email" }
    }
}

// No runtime overhead - unwrapped at compile time
fun getUser(id: UserId): User { /* ... */ }

val userId = UserId(123)
val email = Email("[email protected]")


// ===== TYPE ALIASES =====

typealias UserMap = Map
typealias ClickHandler = (View) -> Unit
typealias Predicate = (T) -> Boolean

val users: UserMap = mapOf(1 to User(1, "Alice", "[email protected]"))
val onClick: ClickHandler = { view -> println("Clicked") }
val isEven: Predicate = { it % 2 == 0 }

Higher-Order Functions & Lambdas

// ===== FUNCTION TYPES =====

val sum: (Int, Int) -> Int = { a, b -> a + b }
val greet: (String) -> Unit = { name -> println("Hello, $name") }
val transform: (String) -> String = { it.uppercase() }


// ===== HIGHER-ORDER FUNCTIONS =====

fun  List.customFilter(predicate: (T) -> Boolean): List {
    val result = mutableListOf()
    for (item in this) {
        if (predicate(item)) {
            result.add(item)
        }
    }
    return result
}

fun  List.customMap(transform: (T) -> R): List {
    return mutableListOf().apply {
        for (item in this@customMap) {
            add(transform(item))
        }
    }
}


// ===== INLINE FUNCTIONS =====

inline fun  measureTime(block: () -> T): Pair {
    val start = System.currentTimeMillis()
    val result = block()
    val time = System.currentTimeMillis() - start
    return result to time
}

// Usage - no lambda allocation overhead
val (result, time) = measureTime {
    // Expensive operation
    Thread.sleep(1000)
    "Done"
}


// ===== NOINLINE & CROSSINLINE =====

inline fun performOperation(
    inline operation: () -> Unit,
    noinline callback: () -> Unit  // Can be stored/passed
) {
    operation()
    callbacks.add(callback)  // OK because noinline
}

inline fun runAsync(crossinline block: () -> Unit) {
    thread {
        block()  // OK because crossinline (no non-local returns)
    }
}


// ===== REIFIED TYPE PARAMETERS =====

inline fun  Any.isInstanceOf(): Boolean {
    return this is T
}

inline fun  Gson.fromJson(json: String): T {
    return fromJson(json, T::class.java)
}

// Usage
val obj: Any = "Hello"
println(obj.isInstanceOf())  // true

val json = """{"name":"Alice"}"""
val user = Gson().fromJson(json)


// ===== SCOPE FUNCTIONS =====

// let - transform object, null-safe calls
val length = "Hello".let { it.length }  // 5
val result = nullableString?.let { it.uppercase() } ?: "DEFAULT"

// run - execute code block with context
val result = "Hello".run {
    println(this)
    length
}

// with - non-extension version of run
val result = with(StringBuilder()) {
    append("Hello")
    append(" ")
    append("World")
    toString()
}

// apply - configure object
val user = User().apply {
    name = "Alice"
    email = "[email protected]"
}

// also - perform additional operations
val numbers = mutableListOf(1, 2, 3).also {
    println("Before: $it")
}.also {
    it.add(4)
}


// ===== FUNCTION REFERENCES =====

fun isEven(num: Int) = num % 2 == 0

val numbers = listOf(1, 2, 3, 4, 5, 6)
val evens = numbers.filter(::isEven)

// Member reference
data class User(val name: String)
val users = listOf(User("Alice"), User("Bob"))
val names = users.map(User::name)

// Constructor reference
val userCreator: (String) -> User = ::User


// ===== TAIL RECURSION =====

tailrec fun factorial(n: Int, accumulator: Int = 1): Int {
    return if (n <= 1) accumulator
    else factorial(n - 1, n * accumulator)
}

// Converted to iterative loop by compiler - no stack overflow
println(factorial(10000))
Pro Tip: Use inline functions with reified type parameters to avoid reflection overhead. Value classes (inline classes) provide type safety without runtime overhead. Prefer extension functions over utility classes for better readability and discoverability.

Coroutines & Structured Concurrency

Coroutine Basics

// ===== SUSPEND FUNCTIONS =====

suspend fun fetchUser(id: Int): User {
    delay(1000)  // Suspends coroutine, doesn't block thread
    return User(id, "Alice", "[email protected]")
}

suspend fun fetchOrders(userId: Int): List {
    delay(500)
    return listOf(Order(1, 100.0), Order(2, 200.0))
}


// ===== LAUNCHING COROUTINES =====

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

// async - returns Deferred
suspend fun getUserData(userId: Int): UserData = coroutineScope {
    val userDeferred = async { fetchUser(userId) }
    val ordersDeferred = async { fetchOrders(userId) }

    UserData(
        user = userDeferred.await(),
        orders = ordersDeferred.await()
    )
}


// ===== COROUTINE BUILDERS =====

// runBlocking - blocks current thread
fun main() = runBlocking {
    println("Start")
    delay(1000)
    println("End")
}

// coroutineScope - suspends, doesn't block
suspend fun doWork() = coroutineScope {
    launch { /* work 1 */ }
    launch { /* work 2 */ }
    // Waits for all children
}

// supervisorScope - children failures don't cancel siblings
suspend fun doWorkWithErrorHandling() = supervisorScope {
    val job1 = launch {
        delay(100)
        throw Exception("Job 1 failed")
    }
    val job2 = launch {
        delay(200)
        println("Job 2 completed")  // Still runs!
    }
}


// ===== COROUTINE CONTEXT & DISPATCHERS =====

// Dispatchers.Default - CPU-intensive work
launch(Dispatchers.Default) {
    val result = calculatePrimes(1000000)
}

// Dispatchers.IO - I/O operations
launch(Dispatchers.IO) {
    val data = File("data.txt").readText()
}

// Dispatchers.Main - UI updates (Android)
launch(Dispatchers.Main) {
    textView.text = "Updated"
}

// Dispatchers.Unconfined - not recommended
launch(Dispatchers.Unconfined) {
    // Starts in caller thread, resumes in arbitrary thread
}


// ===== SWITCHING CONTEXTS =====

suspend fun getData(): String {
    val result = withContext(Dispatchers.IO) {
        // Runs on IO dispatcher
        File("data.txt").readText()
    }
    // Back to previous dispatcher
    return result
}


// ===== CANCELLATION =====

val job = launch {
    repeat(1000) { i ->
        println("Job: I'm working $i")
        delay(500)
    }
}

delay(1300)
println("Cancelling job")
job.cancel()  // Cancel the coroutine
job.join()    // Wait for cancellation


// ===== CANCELLATION COOPERATION =====

val job = launch {
    repeat(1000) { i ->
        // Check if cancelled
        if (!isActive) return@launch

        // Or use ensureActive()
        ensureActive()

        println("Working $i")
        delay(500)
    }
}


// ===== TIMEOUT =====

try {
    withTimeout(1000) {
        delay(2000)  // Will throw TimeoutCancellationException
        println("Done")
    }
} catch (e: TimeoutCancellationException) {
    println("Timed out")
}

// Returns null instead of throwing
val result = withTimeoutOrNull(1000) {
    delay(2000)
    "Done"
}  // null


// ===== EXCEPTION HANDLING =====

// launch - use CoroutineExceptionHandler
val handler = CoroutineExceptionHandler { _, exception ->
    println("Caught exception: $exception")
}

val scope = CoroutineScope(Dispatchers.Default + handler)
scope.launch {
    throw Exception("Error!")  // Caught by handler
}

// async - exceptions stored in Deferred
val deferred = async {
    throw Exception("Error!")
}
try {
    deferred.await()  // Exception thrown here
} catch (e: Exception) {
    println("Caught: $e")
}


// ===== STRUCTURED CONCURRENCY =====

class UserRepository {
    private val scope = CoroutineScope(
        SupervisorJob() + Dispatchers.Default
    )

    fun fetchUsers() {
        scope.launch {
            val users = async { fetchFromDb() }
            val cache = async { updateCache() }

            // Both must complete
            combine(users.await(), cache.await())
        }
    }

    fun cleanup() {
        scope.cancel()  // Cancels all child coroutines
    }
}


// ===== PARALLEL DECOMPOSITION =====

suspend fun loadUserProfile(userId: Int): UserProfile = coroutineScope {
    val user = async { fetchUser(userId) }
    val posts = async { fetchPosts(userId) }
    val friends = async { fetchFriends(userId) }

    UserProfile(
        user = user.await(),
        posts = posts.await(),
        friends = friends.await()
    )
}


// ===== SELECT EXPRESSION (First to complete) =====

suspend fun fetchFromMultipleSources(): String {
    val source1 = async { fetchFromServer1() }
    val source2 = async { fetchFromServer2() }

    return select {
        source1.onAwait { it }
        source2.onAwait { it }
    }
}


// ===== MUTEX (Mutual Exclusion) =====

val mutex = Mutex()
var counter = 0

suspend fun increment() {
    mutex.withLock {
        counter++
    }
}

// Concurrent-safe without blocking
repeat(1000) {
    launch {
        increment()
    }
}

Coroutine Execution Flow

Thread-Blocking vs Suspension:
─────────────────────────────────────────

Thread.sleep(1000):
Thread: [████████████████████] (blocked)
        ^                    ^
        0ms                  1000ms

delay(1000):
Thread: [Task1]─────────[Continue]
        └─(suspended)────┘
        ^                ^
        0ms              1000ms
        (Thread free for other work)


Parallel Execution with async:
─────────────────────────────────────────
launch {
    val user = async { fetchUser() }    ───┐
    val orders = async { fetchOrders() } ──┤
    val profile = async { fetchProfile() }─┤
                                           ├─ Run in parallel
    combine(                               │
        user.await(),                    ──┤
        orders.await(),                  ──┤
        profile.await()                  ──┘
    )
}
                        

Flows & Reactive Programming

Flow Basics

// ===== CREATING FLOWS =====

// Simple flow
fun numbers(): Flow = flow {
    for (i in 1..5) {
        delay(100)
        emit(i)
    }
}

// From collection
val flow = listOf(1, 2, 3).asFlow()

// From channel
val channel = Channel()
val flow = channel.receiveAsFlow()

// flowOf
val flow = flowOf(1, 2, 3, 4, 5)


// ===== COLLECTING FLOWS =====

suspend fun collectNumbers() {
    numbers().collect { value ->
        println(value)
    }
}

// Collect with index
numbers().collectIndexed { index, value ->
    println("[$index] $value")
}

// Collect latest (cancels previous collection)
flow.collectLatest { value ->
    delay(300)
    println(value)  // May skip values
}


// ===== FLOW OPERATORS =====

// map - transform values
val squared = numbers().map { it * it }

// filter - filter values
val evens = numbers().filter { it % 2 == 0 }

// transform - multiple emissions per value
val transformed = numbers().transform { value ->
    emit("Value: $value")
    emit("Squared: ${value * value}")
}

// take - limit emissions
val firstThree = numbers().take(3)

// drop - skip emissions
val afterTwo = numbers().drop(2)

// distinctUntilChanged - skip consecutive duplicates
val distinct = flowOf(1, 1, 2, 2, 3).distinctUntilChanged()  // 1, 2, 3

// debounce - only emit after quiet period
searchQuery.debounce(300).collect { query ->
    search(query)
}

// sample - emit latest value periodically
flow.sample(1000).collect { /* ... */ }


// ===== COMBINING FLOWS =====

// zip - combine values pairwise
val combined = flow1.zip(flow2) { a, b -> a + b }

// combine - combine latest values
val result = flow1.combine(flow2) { a, b -> a + b }

// flatMapConcat - sequential flattening
val concatenated = flow.flatMapConcat { value ->
    flowOf("${value}a", "${value}b")
}

// flatMapMerge - concurrent flattening
val merged = flow.flatMapMerge { value ->
    flow { emit(fetchData(value)) }
}

// flatMapLatest - cancel previous, start new
val latest = searchQuery.flatMapLatest { query ->
    searchDatabase(query)
}


// ===== EXCEPTION HANDLING =====

// catch - handle upstream exceptions
numbers()
    .map { 100 / it }
    .catch { e ->
        println("Caught exception: $e")
        emit(-1)  // Emit fallback value
    }
    .collect { println(it) }

// onCompletion - called when flow completes
flow
    .onCompletion { cause ->
        if (cause != null) println("Flow failed: $cause")
        else println("Flow completed successfully")
    }
    .collect { /* ... */ }


// ===== BUFFERING =====

// buffer - don't wait for collector
flow {
    repeat(5) {
        delay(100)
        emit(it)
    }
}
.buffer()
.collect { value ->
    delay(300)  // Slow collector
    println(value)
}

// conflate - keep only latest
flow.conflate().collect { /* ... */ }


// ===== STATE FLOW (Hot Flow) =====

class UserViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(UiState.Loading)
    val uiState: StateFlow = _uiState.asStateFlow()

    fun loadUser(id: Int) {
        viewModelScope.launch {
            _uiState.value = UiState.Loading
            try {
                val user = userRepository.getUser(id)
                _uiState.value = UiState.Success(user)
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.message)
            }
        }
    }
}

// Collect in UI
lifecycleScope.launch {
    viewModel.uiState.collect { state ->
        when (state) {
            is UiState.Loading -> showLoading()
            is UiState.Success -> showUser(state.user)
            is UiState.Error -> showError(state.message)
        }
    }
}


// ===== SHARED FLOW (Hot Flow, Multiple Subscribers) =====

class EventBus {
    private val _events = MutableSharedFlow()
    val events: SharedFlow = _events.asSharedFlow()

    suspend fun emit(event: Event) {
        _events.emit(event)
    }
}

// Configure replay and buffer
private val _events = MutableSharedFlow(
    replay = 1,          // Replay last N events to new subscribers
    extraBufferCapacity = 64,  // Buffer overflow
    onBufferOverflow = BufferOverflow.DROP_OLDEST
)


// ===== FLOW CONTEXT =====

fun fetchData(): Flow = flow {
    // Runs on context where collect is called
    emit(loadFromDatabase())
}.flowOn(Dispatchers.IO)  // Changes context for upstream

// Usage
withContext(Dispatchers.Main) {
    fetchData().collect { data ->
        // Runs on Main
        updateUI(data)
    }
}


// ===== REAL-WORLD EXAMPLE: SEARCH WITH DEBOUNCE =====

class SearchViewModel : ViewModel() {
    private val _searchQuery = MutableStateFlow("")

    val searchResults: StateFlow> = _searchQuery
        .debounce(300)
        .filter { it.length >= 3 }
        .distinctUntilChanged()
        .flatMapLatest { query ->
            searchRepository.search(query)
                .catch { emit(emptyList()) }
        }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )

    fun onSearchQueryChanged(query: String) {
        _searchQuery.value = query
    }
}


// ===== CHANNEL TO FLOW =====

val channel = Channel()
val flow = channel.receiveAsFlow()

launch {
    repeat(5) {
        channel.send(it)
    }
    channel.close()
}

flow.collect { println(it) }

Flow vs Sequence vs List

Type Execution Async Support Use Case
List Eager (all at once) No Small, in-memory collections
Sequence Lazy (on demand) No Large collections, sync ops
Flow Lazy (on demand) Yes (suspend) Async streams, reactive data
Pro Tip: Use StateFlow for state that always has a value (like UI state). Use SharedFlow for events that may have multiple subscribers. Use regular Flow for cold streams that start emitting when collected. Remember to use .stateIn() or .shareIn() to convert cold flows to hot flows when needed.

Channels & Concurrent Communication

Channel Types & Patterns

// ===== BASIC CHANNEL =====

val channel = Channel()

launch {
    for (i in 1..5) {
        channel.send(i)  // Suspends until received
    }
    channel.close()
}

launch {
    for (value in channel) {  // Iterate until closed
        println(value)
    }
}


// ===== CHANNEL TYPES =====

// Rendezvous (default, capacity = 0)
val rendezvous = Channel()  // send waits for receive

// Buffered (capacity = n)
val buffered = Channel(capacity = 10)  // send only suspends when full

// Unlimited
val unlimited = Channel(Channel.UNLIMITED)  // never suspends on send

// Conflated (keeps only latest)
val conflated = Channel(Channel.CONFLATED)  // overwrites unread values


// ===== PRODUCER-CONSUMER PATTERN =====

fun CoroutineScope.produceNumbers() = produce {
    var x = 1
    while (true) {
        send(x++)
        delay(100)
    }
}

fun CoroutineScope.square(numbers: ReceiveChannel) = produce {
    for (x in numbers) {
        send(x * x)
    }
}

// Usage
val numbers = produceNumbers()
val squares = square(numbers)

repeat(5) {
    println(squares.receive())
}

coroutineContext.cancelChildren()


// ===== FAN-OUT (Multiple consumers) =====

fun CoroutineScope.produceNumbers() = produce {
    var x = 1
    while (true) {
        send(x++)
        delay(100)
    }
}

val producer = produceNumbers()

// Multiple consumers
repeat(5) { id ->
    launch {
        for (msg in producer) {
            println("Consumer #$id received $msg")
        }
    }
}


// ===== FAN-IN (Multiple producers) =====

suspend fun sendString(channel: SendChannel, s: String, time: Long) {
    while (true) {
        delay(time)
        channel.send(s)
    }
}

val channel = Channel()

launch { sendString(channel, "foo", 200) }
launch { sendString(channel, "bar", 500) }

repeat(10) {
    println(channel.receive())
}

coroutineContext.cancelChildren()


// ===== SELECT EXPRESSION WITH CHANNELS =====

suspend fun selectExample() {
    val channel1 = Channel()
    val channel2 = Channel()

    launch {
        delay(100)
        channel1.send("from channel 1")
    }

    launch {
        delay(200)
        channel2.send("from channel 2")
    }

    // Receive from first available
    val result = select {
        channel1.onReceive { it }
        channel2.onReceive { it }
    }

    println(result)  // "from channel 1" (faster)
}


// ===== ACTOR PATTERN =====

sealed class CounterMsg {
    object Increment : CounterMsg()
    object Decrement : CounterMsg()
    data class Get(val response: CompletableDeferred) : CounterMsg()
}

fun CoroutineScope.counterActor() = actor {
    var counter = 0

    for (msg in channel) {
        when (msg) {
            is CounterMsg.Increment -> counter++
            is CounterMsg.Decrement -> counter--
            is CounterMsg.Get -> msg.response.complete(counter)
        }
    }
}

// Usage
val counter = counterActor()

counter.send(CounterMsg.Increment)
counter.send(CounterMsg.Increment)
counter.send(CounterMsg.Decrement)

val response = CompletableDeferred()
counter.send(CounterMsg.Get(response))
println("Counter = ${response.await()}")  // Counter = 1

counter.close()


// ===== BROADCAST CHANNEL (Deprecated - use SharedFlow) =====

// Old way (deprecated)
@OptIn(ObsoleteCoroutinesApi::class)
val broadcast = BroadcastChannel(10)

// New way - use SharedFlow
val sharedFlow = MutableSharedFlow()


// ===== TICKER CHANNEL =====

val tickerChannel = ticker(delayMillis = 1000, initialDelayMillis = 0)

repeat(5) {
    tickerChannel.receive()
    println("Tick")
}

tickerChannel.cancel()


// ===== PIPELINE PATTERN =====

fun CoroutineScope.numbers() = produce {
    var x = 1
    while (true) send(x++)
}

fun CoroutineScope.filter(
    numbers: ReceiveChannel,
    predicate: (Int) -> Boolean
) = produce {
    for (x in numbers) {
        if (predicate(x)) send(x)
    }
}

fun CoroutineScope.map(
    numbers: ReceiveChannel,
    transform: (Int) -> Int
) = produce {
    for (x in numbers) send(transform(x))
}

// Build pipeline
val pipeline = numbers()
    .let { filter(it) { it % 2 == 0 } }
    .let { map(it) { it * it } }

// Consume
repeat(10) {
    println(pipeline.receive())
}

coroutineContext.cancelChildren()
Common Pitfall: Channels are hot - they start producing immediately. Flows are cold - they only start when collected. Don't forget to close() channels when done, or use produce {} builder which closes automatically. Prefer SharedFlow/StateFlow over BroadcastChannel which is now deprecated.

DSL Builders

Type-Safe Builders

// ===== HTML DSL =====

@DslMarker
annotation class HtmlDsl

@HtmlDsl
interface Element {
    fun render(): String
}

@HtmlDsl
class HTML : Element {
    private val children = mutableListOf()

    fun head(init: Head.() -> Unit) {
        children.add(Head().apply(init))
    }

    fun body(init: Body.() -> Unit) {
        children.add(Body().apply(init))
    }

    override fun render() = "${children.joinToString("") { it.render() }}"
}

@HtmlDsl
class Head : Element {
    private val children = mutableListOf()

    fun title(text: String) {
        children.add(Title(text))
    }

    override fun render() = "${children.joinToString("") { it.render() }}"
}

class Title(private val text: String) : Element {
    override fun render() = "$text"
}

@HtmlDsl
class Body : Element {
    private val children = mutableListOf()

    fun h1(text: String) {
        children.add(H1(text))
    }

    fun p(text: String) {
        children.add(P(text))
    }

    fun div(cssClass: String = "", init: Div.() -> Unit) {
        children.add(Div(cssClass).apply(init))
    }

    override fun render() = "${children.joinToString("") { it.render() }}"
}

class H1(private val text: String) : Element {
    override fun render() = "

$text

" } class P(private val text: String) : Element { override fun render() = "

$text

" } @HtmlDsl class Div(private val cssClass: String) : Element { private val children = mutableListOf() fun p(text: String) { children.add(P(text)) } override fun render() { val classAttr = if (cssClass.isNotEmpty()) " class=\"$cssClass\"" else "" return "${children.joinToString("") { it.render() }}
" } } fun html(init: HTML.() -> Unit): HTML { return HTML().apply(init) } // Usage val page = html { head { title("My Page") } body { h1("Welcome") p("This is a paragraph") div("container") { p("Nested content") } } } println(page.render()) // ===== SQL DSL ===== @DslMarker annotation class SqlDsl @SqlDsl class SelectQuery { private val columns = mutableListOf() private var tableName: String = "" private val whereClauses = mutableListOf() private val orderByColumns = mutableListOf() fun select(vararg cols: String) { columns.addAll(cols) } fun from(table: String) { tableName = table } fun where(clause: String) { whereClauses.add(clause) } fun orderBy(vararg cols: String) { orderByColumns.addAll(cols) } fun build(): String { val sql = StringBuilder("SELECT ") sql.append(columns.joinToString(", ")) sql.append(" FROM $tableName") if (whereClauses.isNotEmpty()) { sql.append(" WHERE ") sql.append(whereClauses.joinToString(" AND ")) } if (orderByColumns.isNotEmpty()) { sql.append(" ORDER BY ") sql.append(orderByColumns.joinToString(", ")) } return sql.toString() } } fun query(init: SelectQuery.() -> Unit): String { return SelectQuery().apply(init).build() } // Usage val sql = query { select("id", "name", "email") from("users") where("active = true") where("age > 18") orderBy("name", "email") } // SELECT id, name, email FROM users WHERE active = true AND age > 18 ORDER BY name, email // ===== CONFIGURATION DSL ===== @DslMarker annotation class ConfigDsl @ConfigDsl class ServerConfig { var port: Int = 8080 var host: String = "localhost" var ssl: SslConfig? = null fun ssl(init: SslConfig.() -> Unit) { ssl = SslConfig().apply(init) } } @ConfigDsl class SslConfig { var keyStore: String = "" var keyStorePassword: String = "" } fun server(init: ServerConfig.() -> Unit): ServerConfig { return ServerConfig().apply(init) } // Usage val config = server { port = 9000 host = "0.0.0.0" ssl { keyStore = "keystore.jks" keyStorePassword = "secret" } } // ===== VALIDATION DSL ===== @DslMarker annotation class ValidationDsl @ValidationDsl class ValidationContext { private val rules = mutableListOf>() fun rule(message: String, predicate: (T) -> Boolean) { rules.add(ValidationRule(message, predicate)) } fun validate(value: T): ValidationResult { val errors = rules .filter { !it.predicate(value) } .map { it.message } return if (errors.isEmpty()) { ValidationResult.Valid } else { ValidationResult.Invalid(errors) } } } data class ValidationRule( val message: String, val predicate: (T) -> Boolean ) sealed class ValidationResult { object Valid : ValidationResult() data class Invalid(val errors: List) : ValidationResult() } fun validator(init: ValidationContext.() -> Unit): (T) -> ValidationResult { val context = ValidationContext().apply(init) return { value -> context.validate(value) } } // Usage val emailValidator = validator { rule("Email cannot be empty") { it.isNotEmpty() } rule("Email must contain @") { it.contains("@") } rule("Email must contain domain") { it.split("@").last().contains(".") } } val result = emailValidator("[email protected]") when (result) { is ValidationResult.Valid -> println("Valid!") is ValidationResult.Invalid -> println("Errors: ${result.errors}") } // ===== ROUTING DSL (Ktor-style) ===== @DslMarker annotation class RoutingDsl @RoutingDsl class Routing { private val routes = mutableListOf() fun get(path: String, handler: () -> String) { routes.add(Route("GET", path, handler)) } fun post(path: String, handler: () -> String) { routes.add(Route("POST", path, handler)) } fun route(path: String, init: Routing.() -> Unit) { val nested = Routing().apply(init) routes.addAll(nested.routes.map { it.copy(path = "$path${it.path}") }) } fun findRoute(method: String, path: String): Route? { return routes.find { it.method == method && it.path == path } } } data class Route( val method: String, val path: String, val handler: () -> String ) fun routing(init: Routing.() -> Unit): Routing { return Routing().apply(init) } // Usage val app = routing { get("/") { "Home" } get("/about") { "About" } route("/api") { get("/users") { "List users" } post("/users") { "Create user" } route("/products") { get("/") { "List products" } post("/") { "Create product" } } } }
Pro Tip: Use @DslMarker annotation to prevent nested DSL scopes from calling methods from outer scopes. This makes DSLs type-safe and prevents confusing code. Leverage function types with receivers (T.() -> Unit) for clean DSL syntax.

Android Development Patterns

ViewModel & State Management

// ===== VIEWMODEL WITH STATEFLOW =====

sealed class UiState {
    object Loading : UiState()
    data class Success(val data: List) : UiState()
    data class Error(val message: String) : UiState()
}

class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow(UiState.Loading)
    val uiState: StateFlow = _uiState.asStateFlow()

    init {
        loadUsers()
    }

    fun loadUsers() {
        viewModelScope.launch {
            _uiState.value = UiState.Loading
            try {
                val users = repository.getUsers()
                _uiState.value = UiState.Success(users)
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.message ?: "Unknown error")
            }
        }
    }

    fun refresh() {
        loadUsers()
    }
}

// In Activity/Fragment
class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { state ->
                    when (state) {
                        is UiState.Loading -> showLoading()
                        is UiState.Success -> showUsers(state.data)
                        is UiState.Error -> showError(state.message)
                    }
                }
            }
        }
    }
}


// ===== SINGLE EVENT PATTERN =====

data class UiEvent(
    private val message: String,
    private val id: String = UUID.randomUUID().toString()
) {
    private var hasBeenHandled = false

    fun getContentIfNotHandled(): String? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            message
        }
    }
}

class MyViewModel : ViewModel() {
    private val _events = Channel()
    val events = _events.receiveAsFlow()

    fun triggerEvent() {
        viewModelScope.launch {
            _events.send(UiEvent("Event triggered"))
        }
    }
}

// Collect
lifecycleScope.launch {
    viewModel.events.collect { event ->
        event.getContentIfNotHandled()?.let { message ->
            Toast.makeText(this@MainActivity, message, Toast.LENGTH_SHORT).show()
        }
    }
}


// ===== REPOSITORY PATTERN =====

interface UserRepository {
    suspend fun getUsers(): List
    suspend fun getUser(id: Int): User
    suspend fun createUser(user: User): User
    suspend fun updateUser(user: User)
    suspend fun deleteUser(id: Int)
}

class UserRepositoryImpl(
    private val remoteDataSource: UserRemoteDataSource,
    private val localDataSource: UserLocalDataSource,
    private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : UserRepository {

    override suspend fun getUsers(): List = withContext(dispatcher) {
        try {
            val users = remoteDataSource.fetchUsers()
            localDataSource.saveUsers(users)
            users
        } catch (e: Exception) {
            localDataSource.getUsers()
        }
    }

    override suspend fun getUser(id: Int): User = withContext(dispatcher) {
        localDataSource.getUser(id) ?: remoteDataSource.fetchUser(id)
    }

    // ... other methods
}


// ===== USE CASE PATTERN =====

class GetUserUseCase(
    private val repository: UserRepository,
    private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) {
    suspend operator fun invoke(userId: Int): Result = withContext(dispatcher) {
        try {
            val user = repository.getUser(userId)
            Result.success(user)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

// In ViewModel
class UserDetailViewModel(
    private val getUserUseCase: GetUserUseCase
) : ViewModel() {

    fun loadUser(userId: Int) {
        viewModelScope.launch {
            getUserUseCase(userId)
                .onSuccess { user -> /* handle success */ }
                .onFailure { error -> /* handle error */ }
        }
    }
}


// ===== DEPENDENCY INJECTION WITH HILT =====

@HiltAndroidApp
class MyApplication : Application()

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Provides
    @Singleton
    fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
        return Room.databaseBuilder(
            context,
            AppDatabase::class.java,
            "app_database"
        ).build()
    }

    @Provides
    fun provideUserDao(database: AppDatabase): UserDao {
        return database.userDao()
    }

    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Provides
    fun provideUserApi(retrofit: Retrofit): UserApi {
        return retrofit.create(UserApi::class.java)
    }
}

@Module
@InstallIn(ViewModelComponent::class)
object RepositoryModule {

    @Provides
    fun provideUserRepository(
        api: UserApi,
        dao: UserDao
    ): UserRepository {
        return UserRepositoryImpl(
            remoteDataSource = UserRemoteDataSource(api),
            localDataSource = UserLocalDataSource(dao)
        )
    }
}

// Inject in ViewModel
@HiltViewModel
class UserViewModel @Inject constructor(
    private val repository: UserRepository
) : ViewModel() {
    // ...
}

// Inject in Activity
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()
}

Learning Resources

Tools

  • IntelliJ IDEA / Android Studio
  • Gradle build system
  • Kotlin Playground (online)
  • Android Profiler

Learning Path

  1. Kotlin fundamentals and advanced features
  2. Coroutines and structured concurrency
  3. Flow and reactive programming
  4. Android development with Jetpack
  5. Jetpack Compose for UI
  6. Kotlin Multiplatform basics
  7. Testing and best practices
  8. Production deployment