Variables & Types
val name: String = "Alice"Immutable variable (read-only)var count: Int = 0Mutable variableval name = "Alice"Type inferenceval x: Int? = nullNullable type - appends ?val n: Int = x ?: 0Elvis operator - use default if nullx?.lengthSafe call - returns null if x is nullx!!.lengthNon-null assertion - throws if nullval s: String = x ?: "default"Null coalescing with ElvisInt, Long, Double, Float, Boolean, Char, String, Byte, ShortBuilt-in typesval hex = 0xFF; val bin = 0b1010Hex and binary literalsval big = 1_000_000Underscores in numeric literalsval name: String get() = firstName + " " + lastNameCustom getterStrings
"Hello, $name!"String template - interpolate variable"Result: ${a + b}"String template - interpolate expression"""multi\nline"""Raw (multi-line) string""" hello """.trimIndent()Trim leading whitespace from raw string"hello".uppercase()Convert to uppercase"hello".substring(1, 3)Substring (end exclusive)"a,b,c".split(",")Split to list"hi".repeat(3)Repeat string"hello".contains("ell")Check if contains substring"abc"[0]Access char at index"hello".toInt()Parse string to Int (throws if invalid)"hello".toIntOrNull()Parse string to Int or nullControl Flow
if (x > 0) { ... } else { ... }if / elseval label = if (x > 0) "pos" else "neg"if as expressionwhen (x) { 1 -> "one" 2 -> "two" else -> "other" }when expression - replaces switchwhen { x > 0 -> "pos" x < 0 -> "neg" else -> "zero" }when without argument - condition per branchwhen (x) { is String -> x.length else -> 0 }when with type check and smart castwhen (x) { in 1..10 -> "small" }when with range checkfor (i in 1..10) { }for loop with closed rangefor (i in 1 until 10) { }for loop with open-end range (excludes 10)for (i in 10 downTo 1 step 2) { }Descending range with stepfor (item in list) { }Iterate over collectionfor ((i, v) in list.withIndex()) { }Iterate with indexwhile (x > 0) { }while loopdo { } while (x > 0)do-while loopbreak / continueExit loop / skip iterationbreak@outerBreak outer loop with labelFunctions
fun greet(name: String): String { return "Hi $name" }Function declarationfun greet(name: String) = "Hi $name"Single-expression functionfun greet(name: String = "World"): StringDefault parameterfun greet(name: String, greeting: String = "Hi")Multiple default parametersgreet(greeting = "Hello", name = "Alice")Named arguments - any orderfun sum(vararg nums: Int): IntVararg parametersum(*intArray)Spread operator for varargval double = { x: Int -> x * 2 }Lambda expressionval double: (Int) -> Int = { it * 2 }Lambda with it - single-param shorthandlist.filter { it > 0 }Lambda as trailing argumentfun <T> identity(x: T): T = xGeneric functiontailrec fun fact(n: Int, acc: Int = 1): IntTail-recursive functioninline fun <reified T> check(x: Any) = x is TInline function with reified typeinfix fun Int.times(str: String) = str.repeat(this)Infix functionClasses
class User(val name: String, var age: Int)Primary constructor with propertiesclass User(name: String) { val upper = name.uppercase() }Property initializer in bodyinit { /* runs in constructor */ }Init block - part of primary constructorconstructor(name: String, id: Int) : this(name)Secondary constructorclass Admin : User(name)Inherit from class - call parent constructoropen class Base / final class SubClasses are final by default; open to allow inheritanceoverride fun toString(): StringOverride parent methoddata class Point(val x: Int, val y: Int)Data class - auto equals, hashCode, copy, toStringval p2 = p1.copy(x = 5)Copy data class with modified fieldval (x, y) = pointDestructure data classsealed class ResultSealed class - all subclasses in same fileobject Singleton { fun greet() = "Hi" }Object declaration - singletoncompanion object { val TAG = "User" }Companion object - static-like membersclass Box<T>(val value: T)Generic classenum class Direction { NORTH, SOUTH, EAST, WEST }Enum classenum class Planet(val mass: Double) { EARTH(5.97e24) }Enum with propertiesCollections
listOf(1, 2, 3)Immutable listmutableListOf(1, 2, 3)Mutable listsetOf("a", "b")Immutable setmutableSetOf("a", "b")Mutable setmapOf("a" to 1, "b" to 2)Immutable mapmutableMapOf("a" to 1)Mutable maplist.filter { it > 2 }Filter - keep matching elementslist.map { it * 2 }Map - transform each elementlist.flatMap { listOf(it, it) }FlatMap - map then flattenlist.fold(0) { acc, x -> acc + x }Fold - reduce with initial valuelist.reduce { acc, x -> acc + x }Reduce - fold without initial valuelist.groupBy { it.type }Group into map by key selectorlist.sortedBy { it.name }Sort by propertylist.sortedByDescending { it.age }Sort descendinglist.any { it > 5 }True if any element matcheslist.all { it > 0 }True if all elements matchlist.none { it < 0 }True if no elements matchlist.count { it > 0 }Count matching elementslist.first { it > 2 }First matching element (throws if none)list.firstOrNull { it > 2 }First matching or nulllist.take(3) / list.drop(3)Take first N / drop first N elementslist.zip(other)Pair elements from two listslist.chunked(3)Split into sub-lists of size NCoroutines
suspend fun fetchData(): StringSuspend function - can be pausedrunBlocking { }Run coroutine blocking current thread (testing/main)CoroutineScope(Dispatchers.IO).launch { }Launch fire-and-forget coroutineval result = async { fetchData() }async - returns Deferredval data = result.await()Await deferred resultDispatchers.MainMain/UI thread dispatcherDispatchers.IOIO-optimized dispatcher - network, diskDispatchers.DefaultCPU-intensive work dispatcherwithContext(Dispatchers.IO) { }Switch dispatcher within coroutinedelay(1000)Non-blocking delay (milliseconds)coroutineScope { launch { } launch { } }Structured concurrency - wait for all childrensupervisorScope { }Child failures do not cancel siblingsval flow = flow { emit(1); emit(2) }Cold flow - produces values lazilyflow.collect { println(it) }Collect flow valuesflow.map { }.filter { }.take(5)Flow operatorsExtension Functions & Scope Functions
fun String.shout() = uppercase() + "!"Extension function"hello".shout()Call extension functionval Int.doubled get() = this * 2Extension propertyuser.let { println(it.name) }let - transform or use nullable safelyuser?.let { /* only if non-null */ }let with safe call - null check patternuser.also { log(it) }also - side effect, returns receiveruser.apply { name = "Alice"; age = 30 }apply - configure object, returns receiveruser.run { "${name} is ${age}" }run - transform receiver, returns resultwith(user) { println(name) }with - non-extension run