Kotlin Cheatsheet
Functions
Use this Kotlin reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Function forms
fun add(a: Int, b: Int): Int { // block body — return type required if not Unit return a + b } fun square(x: Int) = x * x // expression body — type inferred fun log(msg: String): Unit { } // Unit is optional to write infix fun Int.pow(e: Int): Long { /* ... */ } // 2 pow 10 tailrec fun gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
Parameters are always read-only vals. Write explicit return types on public APIs.
Default & named arguments
fun greet(name: String, loud: Boolean = false, punct: String = "!") = if (loud) "HELLO, $name$punct" else "Hello, $name$punct" greet("Ada") greet("Ada", punct = "?") // skip middle params by name greet(name = "Ada", loud = true)
Defaults + named args replace most Java-style overloads (see @JvmOverloads for Java callers).
varargs
fun sumAll(vararg xs: Int) = xs.sum() sumAll(1, 2, 3) val arr = intArrayOf(4, 5) sumAll(*arr) // spread operator
Lambdas & function types
val doubled = nums.map { it * 2 } // `it` = single implicit param val kept = nums.filter { n -> n % 2 == 0 } // named param nums.forEach { println(it) } val op: (Int, Int) -> Int = { a, b -> a + b } // function type val nullableFn: ((Int) -> Int)? = null fun apply3(f: (Int) -> Int) = f(3) // higher-order apply3(::square) // function reference list.sortedBy(Point::x) // member reference
A trailing lambda moves outside the parentheses: items.fold(0) { acc, x -> acc + x }. The last expression in a lambda is its value; use return@map value for an early non-local-looking return.
Scope functions
The five stdlib scope functions differ only in how they expose the receiver and what they return:
| Function | Object is | Returns | Typical use |
|---|---|---|---|
let | it | lambda result | transform, null-safe chains: x?.let { } |
run | this | lambda result | configure + compute a result |
with(x) | this | lambda result | group calls on an existing object |
apply | this | the object | object configuration/builders |
also | it | the object | side effects (logging) mid-chain |
val len = name?.let { it.trim().length } ?: 0 val client = HttpClient().apply { // returns the client timeout = 5_000 retries = 3 } val result = with(stats) { "$mean ± $stddev" } val user = loadUser(id) .also { log.debug("loaded {}", it) } .let { it.copy(active = true) } val port = config.run { host.split(":").last().toInt() }
Rules of thumb: apply/also return the object (chain-friendly); let/run/with return the block result. Don't nest them — extract a function instead.
inline, noinline, crossinline
inline fun <T> measure(block: () -> T): T { val t0 = System.nanoTime() try { return block() } finally { println(System.nanoTime() - t0) } }
inlinecopies the function + lambda bodies to the call site: no lambda allocation, andreturninside the lambda exits the caller (non-local return).noinlineon a lambda param opts it out (so it can be stored/passed on).crossinlineforbids non-local returns (needed when the lambda runs in another context).inlinealso unlocksreifiedtype parameters (see Generics & Extensions).
Most stdlib collection operations (map, filter, forEach, scope functions) are inline — zero overhead.