Variables
Introduction
Kotlin is a statically typed language targeting the JVM, Android, and multiplatform projects. This reference covers the essentials.
- Kotlin documentation (kotlinlang.org)
- Kotlin playground (play.kotlinlang.org)
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)
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.
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.
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.
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")
}
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.
Also see
- Kotlin documentation (kotlinlang.org)
- Kotlin API reference (kotlinlang.org)
- Idioms (kotlinlang.org)
- Kotlin playground (play.kotlinlang.org)
- Kotlin GitHub repository (github.com)
0 Comments for this cheatsheet. Write yours!