Language features, idiomatic patterns, and runnable examples that show how Kotlin feels in practice.
when is Kotlin’s powerful replacement for switch, and expressions are first-class.
fun describe(obj: Any): String = when (obj) { 1 -> "One" "Hello" -> "Greeting" is Long -> "Long number" !is String -> "Not a string" else -> "Unknown" } val max = if (a > b) a else b // if is an expression
val numbers = listOf(1, 2, 3, 4, 5, 6) val evens = numbers.filter { it % 2 == 0 } val squared = numbers.map { it * it } val sum = numbers.reduce { acc, n -> acc + n } val grouped = numbers.groupBy { it % 2 == 0 } // {false=[1, 3, 5], true=[2, 4, 6]} numbers .asSequence() .filter { it > 2 } .map { it * 10 } .toList()
Add new functions to existing types without inheritance.
fun String.isPalindrome(): Boolean { val cleaned = this.filter { it.isLetterOrDigit() }.lowercase() return cleaned == cleaned.reversed() } println("A man a plan a canal Panama".isPalindrome()) // true
data class User(val id: Int, val name: String, val email: String) val user = User(1, "Ada", "ada@example.com") val (id, name, email) = user println(user.copy(name = "Augusta")) // User(id=1, name=Augusta, email=ada@example.com)
sealed class Result<out T> { data class Success<T>(val data: T) : Result<T>() data class Error(val message: String) : Result<Nothing>() object Loading : Result<Nothing>() } fun handle(result: Result<String>) = when (result) { is Result.Success -> println(result.data) is Result.Error -> println("Error: ${result.message}") Result.Loading -> println("Loading…") } // no else needed — exhaustive
Lightweight threads with structured concurrency. Add the dependency first:
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
}
import kotlinx.coroutines.* suspend fun fetchUser(id: Int): String { delay(1000) // non-blocking return "User-$id" } fun main() = runBlocking { val deferred = async { fetchUser(42) } println("Doing other work…") println(deferred.await()) }
let, run, with, apply, and also keep code fluent and null-safe.
val result = name?.let { println("Name length: ${it.length}") it.uppercase() } val person = Person().apply { this.name = "Grace" this.age = 45 }.also { println("Created: $it") }
interface Clickable { fun click() fun showOff() = println("I'm clickable!") } class Button : Clickable { override fun click() = println("Button clicked") }
A tiny CLI todo list using a mutable list and simple commands:
fun main() { val todos = mutableListOf<String>() println("Commands: add <text> | list | done <n> | quit") while (true) { print("> ") val input = readlnOrNull() ?: break val parts = input.trim().split(" ", limit = 2) when (parts[0].lowercase()) { "add" -> parts.getOrNull(1)?.let { todos += it; println("Added.") } "list" -> todos.forEachIndexed { i, t -> println("${i + 1}. $t") } "done" -> parts.getOrNull(1)?.toIntOrNull()?.let { if (it in 1..todos.size) { println("Done: ${todos.removeAt(it - 1)}") } } "quit" -> break else -> println("Unknown command") } } }