Kotlin Cheatsheet

Classes

Use this Kotlin reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Classes & constructors

class Counter(private var value: Int = 0) {   // primary constructor in the header
    val history = mutableListOf<Int>()        // property with initializer

    init {                                     // runs after primary constructor
        require(value >= 0)
    }

    constructor(seed: String) : this(seed.length)   // secondary constructor

    fun inc() { value++; history += value }

    var label: String = ""
        get() = field.ifEmpty { "counter" }   // custom accessors; `field` = backing field
        private set                            // public read, private write
}

Visibility: public (default) · internal (module) · protected (subclasses) · private. Classes are final unless marked open.

Inheritance & abstract classes

open class Animal(val name: String) {
    open fun sound() = "..."
    fun describe() = "$name says ${sound()}"   // final unless open
}

class Dog(name: String) : Animal(name) {       // call super constructor
    override fun sound() = "woof"              // override is mandatory
}

abstract class Shape {
    abstract fun area(): Double                // no body, must override
    open fun label() = "shape"
}

super.sound() calls the parent; override fun members are themselves open unless marked final override.

Interfaces

interface Clickable {
    val id: String                       // abstract property
    fun click()                          // abstract
    fun show() = println("show $id")     // default implementation
}

class Button(override val id: String) : Clickable {
    override fun click() = println("clicked")
}

A class implements many interfaces but extends one class. On a diamond conflict, override and disambiguate with super<Clickable>.show().

Data classes

data class User(val name: String, val score: Int = 0)

val ada = User("Ada", 42)
val next = ada.copy(score = 43)          // structural copy
val (name, score) = ada                  // destructuring via componentN()

Generates equals/hashCode/toString/copy/component1..N from primary-constructor properties only. Destructuring also works in lambdas: users.map { (name, _) -> name } and with Pair/Triple (val (a, b) = 1 to 2).

Sealed classes & interfaces

sealed interface ApiResult<out T>
data class Ok<T>(val value: T) : ApiResult<T>
data class Err(val code: Int, val msg: String) : ApiResult<Nothing>
data object Loading : ApiResult<Nothing>      // data object: singleton with toString/equals

fun <T> render(r: ApiResult<T>) = when (r) {  // exhaustive — compiler checks, no else
    is Ok -> "value=${r.value}"
    is Err -> "error ${r.code}"
    Loading -> "…"
}

All direct subtypes must live in the same module/package, so when can be exhaustive. Adding a subtype turns every non-exhaustive when into a compile error — this is the Kotlin way to model closed unions.

Enum classes

enum class Direction(val dx: Int, val dy: Int) {
    NORTH(0, -1), SOUTH(0, 1), EAST(1, 0), WEST(-1, 0);

    fun opposite() = when (this) {
        NORTH -> SOUTH; SOUTH -> NORTH; EAST -> WEST; WEST -> EAST
    }
}

Direction.entries              // List<Direction> (Kotlin 1.9+; prefer over values())
Direction.valueOf("NORTH")     // throws on unknown
Direction.NORTH.ordinal; Direction.NORTH.name

object & companion object

object AppConfig {                        // singleton, lazily initialized
    val version = "2.2"
}

class Parser private constructor() {
    companion object {                    // "statics" live here
        const val MAX_DEPTH = 64
        fun fromString(s: String) = Parser()   // factory pattern
    }
}
Parser.fromString("x")                    // called through the class name

val handler = object : Runnable {         // anonymous object (like anon inner class)
    override fun run() { }
}

Delegation

// Interface delegation: forward an interface to a field with `by`
class Tracked(private val inner: MutableList<Int>) : MutableList<Int> by inner {
    override fun add(e: Int): Boolean { println("add $e"); return inner.add(e) }
}

// Property delegates
val config: Config by lazy { loadConfig() }        // computed once, thread-safe by default

import kotlin.properties.Delegates
var score: Int by Delegates.observable(0) { _, old, new -> println("$old -> $new") }
var positive: Int by Delegates.vetoable(1) { _, _, new -> new > 0 }
val title: String by map                            // read property from a Map