Kotlin is a statically typed language targeting the JVM, Android, and multiplatform projects. This reference covers variables, null safety, collections, functions, and classes.

Variables

Introduction

Kotlin is a statically typed language targeting the JVM, Android, and multiplatform projects. This reference covers the essentials.

Mutability

var mutableString: String = "Adam"
val immutableString: String = "Adam"
val inferredString = "Adam"

Use var for values that change, val for read-only ones. Types are inferred unless annotated.

See: Variables

Strings

val name = "Adam"
val greeting = "Hello, " + name
val template = "Hello, $name"
val upper = "Hi, ${name.uppercase()}"

String templates interpolate values with $name or ${expression}.

See: String templates

Numbers

val intNum = 10
val doubleNum = 10.0
val longNum = 10L
val floatNum = 10.0F

See: Numbers

Booleans

val isReady = true
val isBusy = false
val andCondition = isReady && isBusy
val orCondition = isReady || isBusy

See: Booleans

Companion objects

class Person {
    companion object {
        const val NAME_KEY = "name_key"
    }
}

val key = Person.NAME_KEY

Members are accessed through the class name, like statics in Java. Use const val for compile-time constants.

See: Companion objects

Null safety

Nullable types

val cannotBeNull: String = null  // compile error
val canBeNull: String? = null    // ok

A ? after the type marks it as nullable; non-nullable types reject null.

See: Null safety

Checking for null

val name: String? = "Adam"

if (name != null) {
    print("Length: ${name.length}")
} else {
    print("String is null")
}

After the null check, name is smart-cast to a non-nullable String.

See: Checking for null

Safe call operator

val length: Int? = name?.length
val head: String? = person?.department?.head?.name

?. returns null instead of throwing when the receiver is null.

See: Safe calls

Elvis operator

val length: Int = name?.length ?: 0
val head: String = person?.department?.head?.name ?: ""
val upper: String = name?.uppercase().orEmpty()  // "" if null

?: returns the left side when it isn't null, the right side otherwise. orEmpty() is shorthand for ?: "".

See: Elvis operator

Not-null assertion

val length: Int = name!!.length

!! throws a NullPointerException when the value is null. Prefer ?., ?:, or requireNotNull().

See: Not-null assertion

Safe casts

// Returns null instead of throwing ClassCastException
val car: Car? = input as? Car

See: Safe casts

Collections

Creation

val numArray = arrayOf(1, 2, 3)
val numList = listOf(1, 2, 3)
val numSet = setOf(1, 2, 3)
val mutableNumList = mutableListOf(1, 2, 3)

See: Constructing collections

Accessing

val byIndex = numList[0]                 // throws if empty
val first = numList.first()              // throws if empty
val firstOrNull = numList.firstOrNull()  // null if empty
val last = numList.last()

See: Retrieving elements

Maps

val faceCards = mutableMapOf("Jack" to 11, "Queen" to 12)
val jackValue = faceCards["Jack"]  // 11, or null if missing
faceCards["Ace"] = 1

val immutableMap = mapOf("Jack" to 11, "Queen" to 12)

Reads return null for missing keys; getValue() throws instead.

See: Map operations

Mutability

val immutableList = listOf(1, 2, 3)
val mutableList = immutableList.toMutableList()

val immutableMap = mapOf("Jack" to 11, "Queen" to 12)
val mutableMap = immutableMap.toMutableMap()

listOf() and mapOf() are read-only; use the mutable* builders to add, set, or remove.

See: Collection types

Iterating

for (item in myList) {
    print(item)
}

myList.forEach {
    print(it)
}

myList.forEachIndexed { index, item ->
    print("Item at $index is: $item")
}

See: Iterators

Filtering & searching

val evens = numList.filter { it % 2 == 0 }
val hasEven = numList.any { it % 2 == 0 }
val allOdd = numList.all { it % 2 != 0 }
val noEvens = numList.none { it % 2 == 0 }
val firstEven = numList.first { it % 2 == 0 }  // throws if none
val firstEvenOrNull = numList.firstOrNull { it % 2 == 0 }

it is the implicit name of a lambda's single parameter.

See: Filtering collections

Transforming & grouping

data class Item(val name: String, val price: Int)

val items = listOf(Item("Coffee", 3), Item("Tea", 2))

val menu = items.map { "${it.name} - ${it.price}" }
val byName = items.groupBy { it.name }
val sorted = items.sortedByDescending { it.price }
val total = items.sumOf { it.price }

See: Transformations, Grouping, Ordering

Functions

Parameters & return types

Block body

fun printName(name: String) {
    print(name)
}

fun getGreeting(person: Person): String {
    return "Hello, ${person.name}"
}

Expression body

fun getGreeting(person: Person) = "Hello, ${person.name}"

Block bodies only need a return type when they return a value; expression bodies infer it.

See: Functions

Higher-order functions

fun callbackIfTrue(condition: Boolean, callback: () -> Unit) {
    if (condition) {
        callback()
    }
}

callbackIfTrue(someBoolean) {
    print("Condition was true")
}

Functions are values: () -> Unit is a function type that callback parameters accept.

See: Higher-order functions

Extension functions

fun Int.timesTwo(): Int {
    return this * 2
}

val four = 2.timesTwo()

Extensions add method-like call syntax without modifying the type; calls are resolved statically.

See: Extensions

Default parameters

fun getGreeting(person: Person, intro: String = "Hello,"): String {
    return "$intro ${person.name}"
}

// Returns "Hello, Adam"
val hello = getGreeting(Person("Adam"))

// Returns "Welcome, Adam"
val welcome = getGreeting(Person("Adam"), "Welcome,")

See: Default arguments

Named parameters

class Person(val name: String = "", val age: Int = 0)

// All valid
val a = Person()
val b = Person("Adam", 100)
val c = Person(name = "Adam", age = 100)
val d = Person(age = 100)
val e = Person(age = 100, name = "Adam")

See: Named arguments

Companion functions

class Logger(val tag: String) {
    companion object {
        fun create(tag: String): Logger {
            return Logger(tag)
        }
    }
}

val log = Logger.create("app")

Logger.create() is called on the class, like a static factory method in Java. Add @JvmStatic so Java can call it as Logger.create("app").

See: Companion objects

Classes

Primary constructor

class Person(val name: String, val age: Int)

val adam = Person("Adam", 100)

Constructor parameters marked val or var become properties.

See: Primary constructor

Secondary constructors

Delegating constructor

class Person(val name: String) {
    private var age: Int? = null

    constructor(name: String, age: Int) : this(name) {
        this.age = age
    }
}

Default parameters

class Person(val name: String, val age: Int? = null)

Secondary constructors delegate to the primary one with : this(...); default parameters often replace them.

See: Secondary constructors

Inheritance & implementation

open class Vehicle
class Car : Vehicle()

interface Runner {
    fun run()
}

class Machine : Runner {
    override fun run() {
        print("Running")
    }
}

Classes are final by default; mark them open to allow subclassing. Interfaces are implemented with :.

See: Inheritance, Interfaces

Control flow

If statements

if (isDone) {
    print("Done")
} else {
    print("Pending")
}

val label = if (isDone) "Done" else "Pending"

if is an expression: it returns the value of the taken branch.

See: If expression

For loops

for (i in 0..10) { }              // 0 to 10, inclusive
for (i in 0 until 10) { }         // 0 to 9
for (i in 0..<10) { }             // 0 to 9, same as until
for (i in 10 downTo 0 step 2) { }  // 10, 8, 6, ...
(0..10).forEach { }

See: For loops

When statements

With a subject

enum class Direction { NORTH, SOUTH, EAST, WEST }

when (direction) {
    Direction.NORTH -> print("North")
    Direction.SOUTH -> print("South")
    Direction.EAST, Direction.WEST -> print("East or West")
    else -> print("Invalid direction")
}

Without a subject

when {
    x > 0 -> print("Positive")
    x < 0 -> print("Negative")
    else -> print("Zero")
}

when matches by value, type, or condition. With no argument it acts like if/else-if.

See: When expressions

While loops

while (x > 0) {
    x--
}

do {
    x--
} while (x > 0)

See: While loops

Destructuring declarations

Objects & lists

Destructuring reads componentN() functions, so it works with data classes, pairs, arrays, and maps.

Data classes

data class Person(val name: String, val age: Int)

val person = Person("Adam", 100)
val (name, age) = person

Pairs & arrays

val pair = Pair(1, 2)
val (first, second) = pair

val coordinates = arrayOf(1, 2, 3)
val (x, y, z) = coordinates

Maps

val scores = mapOf("Adam" to 100)
for ((key, value) in scores) {
    print("$key = $value")
}

See: Destructuring declarations

ComponentN functions

class Person(val name: String, val age: Int) {
    operator fun component1(): String {
        return name
    }

    operator fun component2(): Int {
        return age
    }
}

Declare operator fun componentN() to make a class destructurable. data class generates them for you.

See: Destructuring declarations

Also see

0 Comments for this cheatsheet. Write yours!