Kotlin
Kotlin cheat sheet covering syntax, null safety, coroutines, data classes, extensions, and Android development patterns with examples.
Getting Started
Kotlin basics and fundamentals
Variable declarations and basic types
// Immutable variable (read-only)
val name: String = "Kotlin"
val age = 25 // Type inference
// name = "Java" // Error: Val cannot be reassigned
// Mutable variable
var count = 0
count++ // OK
count = 10 // OK
// Basic types
val byte: Byte = 127
val short: Short = 32767
val int: Int = 2147483647
val long: Long = 9223372036854775807L
val float: Float = 3.14f
val double: Double = 3.141592653589793
val boolean: Boolean = true
val char: Char = 'K'
val string: String = "Hello, Kotlin"String interpolation and manipulation
// String templates
val name = "Alice"
val age = 30
val message = "Hello, $name! You are $age years old."
val expression = "Next year you'll be ${age + 1}"
// Multiline strings
val multiline = """
|First line
|Second line
|Third line
""".trimMargin()
val rawString = """
No escaping needed:
\
Preserves formatting
"""
// String operations
val str = "Kotlin"
println(str.length) // 6
println(str.uppercase()) // KOTLIN
println(str[0]) // K
println(str.substring(0, 3)) // KotNull Safety
Kotlin null safety system
Working with nullable types safely
// Nullable types
var nullable: String? = "Hello"
nullable = null // OK
var nonNull: String = "Hello"
// nonNull = null // Error: Null cannot be assigned
// Safe call operator
val length = nullable?.length // Returns null if nullable is null
// Elvis operator
val lengthOrZero = nullable?.length ?: 0
val message = nullable ?: "Default message"
// Not-null assertion (use sparingly!)
val forceLength = nullable!!.length // Throws NPE if null
// Safe casts
val any: Any = "String"
val str: String? = any as? String // Safe cast
val num: Int? = any as? Int // Returns nullAdvanced null handling patterns
// Scoped functions for null handling
val str: String? = "Hello"
// let - execute block if not null
str?.let {
println("Length: ${it.length}")
}
// also - side effects
str?.also {
println("Processing: $it")
}?.uppercase()
// run - execute block with receiver
val result = str?.run {
println("Running on $this")
length * 2
}
// takeIf / takeUnless
val email = "user@example.com"
val validEmail = email.takeIf { it.contains("@") }
val invalidEmail = email.takeUnless { it.contains("@") }Functions
Function declarations and features
Function syntax and parameters
// Basic function
fun greet(name: String): String {
return "Hello, $name!"
}
// Single expression function
fun add(a: Int, b: Int) = a + b
// Default parameters
fun connect(host: String = "localhost", port: Int = 8080) {
println("Connecting to $host:$port")
}
// Named arguments
connect(port = 3000, host = "example.com")
// Unit return type (void)
fun printMessage(msg: String): Unit {
println(msg)
}
// Unit can be omitted
fun log(msg: String) {
println("[LOG] $msg")
}Functions as parameters and lambdas
// Lambda expressions
val sum = { x: Int, y: Int -> x + y }
val square: (Int) -> Int = { it * it }
// Higher-order function
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
return operation(x, y)
}
val result = calculate(10, 5) { a, b -> a * b }
// Function with receiver
fun buildString(action: StringBuilder.() -> Unit): String {
val sb = StringBuilder()
sb.action()
return sb.toString()
}
val html = buildString {
append("<h1>")
append("Title")
append("</h1>")
}
// Inline functions
inline fun measureTime(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}Classes & Objects
Object-oriented programming in Kotlin
Class declarations and constructors
// Primary constructor
class Person(val name: String, var age: Int)
// Full syntax with init block
class Student(firstName: String, lastName: String) {
val fullName: String
var grade: Int = 0
init {
fullName = "$firstName $lastName"
println("Student created: $fullName")
}
// Secondary constructor
constructor(name: String) : this(name, "") {
println("Secondary constructor called")
}
}
// Properties with getters/setters
class Temperature {
var celsius: Double = 0.0
get() = field
set(value) {
field = if (value < -273.15) -273.15 else value
}
val fahrenheit: Double
get() = celsius * 9/5 + 32
}Class inheritance and polymorphism
// Open class (can be inherited)
open class Animal(val name: String) {
open fun makeSound() {
println("Some generic animal sound")
}
fun sleep() {
println("$name is sleeping")
}
}
// Inheritance
class Dog(name: String, val breed: String) : Animal(name) {
override fun makeSound() {
println("$name barks: Woof!")
}
fun wagTail() {
println("$name is wagging tail")
}
}
// Abstract classes
abstract class Shape {
abstract val area: Double
abstract fun draw()
fun describe() {
println("Area: $area")
}
}
class Circle(private val radius: Double) : Shape() {
override val area = Math.PI * radius * radius
override fun draw() {
println("Drawing circle with radius $radius")
}
}Data Classes & Objects
Data classes, objects, and companions
Classes for holding data
// Data class
data class User(
val id: Int,
val name: String,
var email: String
)
val user = User(1, "Alice", "alice@example.com")
val copy = user.copy(email = "newemail@example.com")
// Destructuring
val (id, name, email) = user
// Generated methods
println(user.toString()) // User(id=1, name=Alice, email=alice@example.com)
println(user.hashCode())
println(user == copy) // false (different email)
// Data class requirements
// - Primary constructor with at least one parameter
// - All parameters marked as val or var
// - Cannot be abstract, open, sealed, or innerSingleton objects and companion objects
// Object declaration (Singleton)
object DatabaseConnection {
init {
println("Initializing database connection")
}
fun connect() {
println("Connecting to database...")
}
}
// Usage
DatabaseConnection.connect()
// Companion object
class Factory {
companion object {
private var counter = 0
fun create(): Factory {
counter++
return Factory()
}
fun getCount() = counter
}
}
val instance = Factory.create()
val count = Factory.getCount()
// Named companion
class MyClass {
companion object Loader {
fun load() = MyClass()
}
}Collections
Lists, Sets, Maps and operations
Lists, Sets, and Maps
// Immutable collections
val list = listOf(1, 2, 3)
val set = setOf("a", "b", "c")
val map = mapOf("key1" to "value1", "key2" to "value2")
// Mutable collections
val mutableList = mutableListOf(1, 2, 3)
mutableList.add(4)
mutableList.removeAt(0)
val mutableSet = mutableSetOf("a", "b")
mutableSet.add("c")
val mutableMap = mutableMapOf<String, Int>()
mutableMap["one"] = 1
mutableMap["two"] = 2
// Array types
val intArray = intArrayOf(1, 2, 3)
val stringArray = arrayOf("a", "b", "c")
val nullArray = arrayOfNulls<String>(5)Transformations and aggregations
// Transformations
val numbers = listOf(1, 2, 3, 4, 5)
val doubled = numbers.map { it * 2 }
val filtered = numbers.filter { it > 2 }
val strings = numbers.map { "Item $it" }
// Aggregations
val sum = numbers.sum()
val average = numbers.average()
val max = numbers.maxOrNull()
val count = numbers.count { it % 2 == 0 }
// Grouping
val words = listOf("apple", "apricot", "banana", "blueberry")
val grouped = words.groupBy { it.first() }
// {a=[apple, apricot], b=[banana, blueberry]}
// Flattening
val nested = listOf(listOf(1, 2), listOf(3, 4))
val flat = nested.flatten() // [1, 2, 3, 4]
val flatMapped = nested.flatMap { it.map { n -> n * 2 } } // [2, 4, 6, 8]Control Flow
Conditionals and loops
if, when, and conditional expressions
// If expression
val max = if (a > b) a else b
// If-else chain
val result = if (score >= 90) {
"A"
} else if (score >= 80) {
"B"
} else if (score >= 70) {
"C"
} else {
"F"
}
// When expression (like switch)
when (x) {
1 -> println("One")
2 -> println("Two")
3, 4 -> println("Three or Four")
in 5..10 -> println("Between 5 and 10")
!in 10..20 -> println("Not between 10 and 20")
is String -> println("It's a string")
else -> println("Unknown")
}
// When as expression
val description = when (val code = getCode()) {
200 -> "OK"
404 -> "Not Found"
500 -> "Server Error"
else -> "Unknown code: $code"
}for, while, and loop control
// For loops
for (i in 1..5) {
println(i) // 1, 2, 3, 4, 5
}
for (i in 1 until 5) {
println(i) // 1, 2, 3, 4
}
for (i in 5 downTo 1) {
println(i) // 5, 4, 3, 2, 1
}
for (i in 1..10 step 2) {
println(i) // 1, 3, 5, 7, 9
}
// Iterating collections
val list = listOf("a", "b", "c")
for (item in list) {
println(item)
}
for ((index, value) in list.withIndex()) {
println("$index: $value")
}
// While loops
var x = 5
while (x > 0) {
println(x--)
}
// Do-while
do {
val y = readLine()
} while (y != "exit")Extensions
Extension functions and properties
Adding functions to existing types
// Extension function
fun String.removeSpaces(): String {
return this.replace(" ", "")
}
val text = "Hello World"
println(text.removeSpaces()) // HelloWorld
// Extension properties
val String.lastChar: Char
get() = this[length - 1]
println("Kotlin".lastChar) // n
// Extensions on nullable types
fun String?.isNullOrEmpty(): Boolean {
return this == null || this.isEmpty()
}
val nullString: String? = null
println(nullString.isNullOrEmpty()) // true
// Generic extensions
fun <T> List<T>.secondOrNull(): T? {
return if (size >= 2) this[1] else null
}let, run, with, apply, also
// let - it as argument, returns result
val result = "Hello".let {
println("Original: $it")
it.uppercase()
} // HELLO
// Null safety with let
val nullableString: String? = "Kotlin"
nullableString?.let {
println("Length: ${it.length}")
}
// run - this as receiver, returns result
val formatted = "hello".run {
println("Original: $this")
uppercase()
} // HELLO
// with - non-extension, returns result
val numbers = mutableListOf(1, 2, 3)
val sum = with(numbers) {
add(4)
add(5)
sum()
}
// apply - this as receiver, returns receiver
val person = Person().apply {
name = "Alice"
age = 30
email = "alice@example.com"
}
// also - it as argument, returns receiver
val list = mutableListOf(1, 2, 3).also {
println("Adding items to list: $it")
it.add(4)
}Coroutines
Asynchronous programming with coroutines
Launching and managing coroutines
// Coroutine scope and launch
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000)
println("World")
}
println("Hello")
}
// Async and await
suspend fun fetchUser(): User {
delay(1000) // Simulated network call
return User("Alice")
}
suspend fun fetchPosts(): List<Post> {
delay(1000)
return listOf(Post("Title"))
}
fun main() = runBlocking {
val user = async { fetchUser() }
val posts = async { fetchPosts() }
println("User: ${user.await()}")
println("Posts: ${posts.await()}")
}
// Suspend functions
suspend fun doWork() {
delay(1000)
println("Work done")
}Dispatchers and coroutine context
// Dispatchers
fun main() = runBlocking {
launch(Dispatchers.Main) {
// UI updates (Android)
}
launch(Dispatchers.IO) {
// IO operations
val data = readFile()
}
launch(Dispatchers.Default) {
// CPU-intensive work
val result = complexCalculation()
}
launch(Dispatchers.Unconfined) {
// Not confined to any thread
}
}
// withContext - switching context
suspend fun fetchData(): String = withContext(Dispatchers.IO) {
// Perform IO operation
readFromNetwork()
}
suspend fun processData() = withContext(Dispatchers.Default) {
// CPU-intensive processing
parseData()
}
// Flow - cold asynchronous stream
fun numbersFlow(): Flow<Int> = flow {
for (i in 1..5) {
delay(100)
emit(i)
}
}
fun main() = runBlocking {
numbersFlow()
.map { it * it }
.filter { it % 2 == 0 }
.collect { println(it) }
}Generics
Generic types and variance
Type parameters and constraints
// Generic class
class Box<T>(val value: T) {
fun get(): T = value
}
val intBox = Box(42)
val stringBox = Box("Hello")
// Generic function
fun <T> singletonList(item: T): List<T> {
return listOf(item)
}
val list = singletonList("item")
// Multiple type parameters
class Pair<A, B>(val first: A, val second: B) {
fun swap(): Pair<B, A> = Pair(second, first)
}
// Type constraints
fun <T : Comparable<T>> max(a: T, b: T): T {
return if (a > b) a else b
}
// Multiple constraints with where
fun <T> process(value: T)
where T : CharSequence,
T : Comparable<T> {
println("Length: ${value.length}")
println("Value: $value")
}Delegation
Class and property delegation
Class delegation and delegated properties
// Class delegation
interface Base {
fun print()
val message: String
}
class BaseImpl(val x: Int) : Base {
override fun print() = println(x)
override val message = "BaseImpl: $x"
}
class Derived(b: Base) : Base by b {
// Can override members
override val message = "Derived message"
}
val base = BaseImpl(10)
val derived = Derived(base)
derived.print() // Delegates to base
// Delegated properties
class User {
// Lazy property
val lazyValue: String by lazy {
println("Computing lazy value")
"Hello"
}
// Observable property
var name: String by Delegates.observable("Initial") {
prop, old, new ->
println("$old -> $new")
}
// Vetoable property
var age: Int by Delegates.vetoable(0) {
prop, old, new ->
new >= 0 // Reject negative values
}
}DSL Building
Creating domain-specific languages
Building type-safe DSLs
// HTML DSL example
class HTML {
private val children = mutableListOf<Element>()
fun body(init: Body.() -> Unit) {
val body = Body()
body.init()
children.add(body)
}
}
class Body : Element() {
fun h1(text: String, init: H1.() -> Unit = {}) {
val h1 = H1(text)
h1.init()
children.add(h1)
}
fun p(text: String) {
children.add(P(text))
}
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML()
html.init()
return html
}
// Usage
val doc = html {
body {
h1("Title") {
// H1 configuration
}
p("Paragraph text")
}
}Annotations & Reflection
Custom annotations and reflection
Creating and using annotations
// Annotation declaration
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class MyAnnotation(
val name: String,
val version: Int = 1
)
// Using annotations
@MyAnnotation(name = "Example", version = 2)
class AnnotatedClass {
@Deprecated("Use newMethod instead")
fun oldMethod() {}
@JvmStatic
fun staticMethod() {}
@Throws(IOException::class)
fun riskyMethod() {
throw IOException("Error")
}
}
// Built-in annotations
class Example {
@JvmField
val field = "Public field in Java"
@JvmOverloads
fun method(a: String = "default") {}
@Suppress("UNCHECKED_CAST")
fun uncheckedCast(obj: Any): List<String> {
return obj as List<String>
}
}Interoperability
Java interop and platform types
Calling Java from Kotlin and vice versa
// Calling Java from Kotlin
val list = ArrayList<String>() // Java class
list.add("item")
list.remove("item")
// Platform types
val item = list[0] // Platform type String!
// Can be treated as nullable or non-null
val nullable: String? = item
val nonNull: String = item // May throw NPE
// Java getters/setters as properties
// Java: class Person { getName(); setName(String) }
val person = Person()
person.name = "Alice" // Calls setName
val name = person.name // Calls getName
// Static members
val result = Math.max(5, 10) // Java static method
val pi = Math.PI // Java static field
// SAM conversion
val runnable = Runnable {
println("Running")
}
button.setOnClickListener { view ->
// SAM conversion for Java interfaces
}Advanced Features
Inline classes, contracts, and more
Performance optimizations
// Inline functions
inline fun measureTimeMillis(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}
// No function call overhead
val time = measureTimeMillis {
// Code is inlined here
Thread.sleep(100)
}
// Inline classes (value classes)
@JvmInline
value class UserId(val id: Long) {
init {
require(id > 0) { "Id must be positive" }
}
val displayId: String
get() = "USER_$id"
}
// No boxing overhead at runtime
fun getUser(userId: UserId) {
// userId is passed as primitive long
}
// Crossinline and noinline
inline fun inlineFunc(
crossinline body: () -> Unit, // Can't use return
noinline callback: () -> Unit // Not inlined
) {
val runnable = object : Runnable {
override fun run() {
body() // OK with crossinline
}
}
callback() // Regular function call
}