This cheat sheet covers the essential Kotlin syntax students need when reading, writing, and debugging modern Kotlin programs. It is useful as a quick classroom reference because Kotlin uses concise patterns that can be easy to forget at first. Students can use it to compare variable declarations, functions, control flow, classes, and Android starter code without searching through full documentation.
The most important ideas are type inference, null safety, object-oriented structure, collection operations, and coroutine basics. Kotlin uses val for read-only variables, var for mutable variables, fun for functions, and class or data class for defining types. Null safety relies on ?, ?:, and ?. to prevent common runtime crashes.
Android Kotlin code often uses lifecycle methods, view setup, listeners, and ViewModel patterns to organize app behavior.
Key Facts
- Use val name = "Ada" for a read-only variable and var score = 0 for a variable whose value can change.
- Declare a function with fun add(a: Int, b: Int): Int { return a + b } or use an expression body with fun add(a: Int, b: Int) = a + b.
- Use if as an expression, such as val result = if (score >= 60) "pass" else "retry".
- Use a nullable type with String? and safely access it with name?.length or provide a default with name ?: "Unknown".
- Create a data class with data class User(val id: Int, val name: String) to automatically get useful functions like toString and copy.
- Loop through a range with for (i in 1..5) and loop through a collection with for (item in items).
- Use listOf("a", "b") for a read-only list and mutableListOf("a", "b") when elements must be added or removed.
- Launch coroutine work from a scope with scope.launch { } and call suspend functions only from another suspend function or coroutine.
Vocabulary
- val
- A Kotlin keyword for declaring a read-only variable that cannot be reassigned after it receives a value.
- var
- A Kotlin keyword for declaring a mutable variable that can be reassigned later in the program.
- Nullable type
- A type marked with ? that allows a variable to hold either a normal value or null.
- Data class
- A class mainly used to store data and automatically provide functions such as equals, toString, and copy.
- Coroutine
- A Kotlin feature for running asynchronous or long-running work without blocking the main thread.
- Lambda
- An anonymous function written as a value, often used with collection operations and event listeners.
Common Mistakes to Avoid
- Using var when val is enough is a mistake because it allows reassignment that the program may not need, making bugs easier to introduce.
- Forgetting the ? on a value that may be null is a mistake because Kotlin will not let a non-null type store null, and unsafe workarounds can cause crashes.
- Calling a suspend function from normal code is a mistake because suspend functions must run inside another suspend function or a coroutine scope.
- Confusing == and === is a mistake because == checks value equality while === checks whether two references point to the exact same object.
- Updating Android UI work from a background thread is a mistake because UI changes must happen on the main thread to avoid errors and unpredictable behavior.
Practice Questions
- 1 Write Kotlin code that declares a read-only variable age with the value 17 and a mutable variable points with the value 0, then increases points by 10.
- 2 Given val numbers = listOf(2, 4, 6, 8), what is the result of numbers.map { it * 3 }?
- 3 Write a Kotlin function named isPassing that takes score: Int and returns true when score is at least 60.
- 4 Explain why Kotlin requires safe calls like user?.name when user has the type User?, and how this helps prevent program crashes.
Understanding Kotlin Syntax Quick Reference
Kotlin tries to make the programmer state intent clearly. A read-only reference tells other people that the variable will not be assigned a different value later. That does not always mean the object stored in it cannot change.
A read-only reference to a mutable list can still point to a list whose contents are edited. This difference matters when tracking bugs caused by shared data. Type inference saves typing, but explicit types are useful when a value crosses an important boundary, such as a function result or a public property.
Clear names and small scopes make inferred types easier to understand. Students should watch for variables with similar names in nested blocks, since a local name can hide an outer one.
Kotlin's null rules are really a way to model missing information. An empty string, zero, and an absent value mean different things. Treating them as the same can create confusing program behavior.
The compiler requires evidence before code uses a value that might be absent. After a direct check, it can often recognize that the value is safe within that block. This is called a smart cast.
Code that comes from Java needs extra care because Java may not describe whether a value can be absent. A program may compile while still receiving an unexpected null from a library. Good programs decide early whether missing input should stop an action, use a sensible fallback, or show a message to the user.
Collections are central to real programs because apps usually handle groups of records rather than one value at a time. Operations such as filtering, mapping, grouping, and sorting describe a data pipeline. Each step should have one clear job.
For instance, a program can select completed tasks, turn them into display text, then sort the text for a screen. Students should notice whether an operation creates a new collection or changes an existing one. Creating new results often makes code safer to reason about.
Sequences can delay work until results are needed, which helps with large data sets, though they can make debugging less direct. Data classes work well for records because two instances with the same property values are normally treated as equal. That behavior is helpful when testing lists, comparing results, or updating screen state.
Coroutines make waiting less wasteful. Network requests, file reading, and database work may take time, but the program should not freeze while it waits. A suspend function can pause its coroutine and later continue from the same point.
Suspending does not automatically move work to a background thread. The chosen dispatcher determines where the work runs. In Android, coroutine work should belong to a lifecycle-aware scope so it is cancelled when a screen is destroyed.
This prevents old tasks from trying to update views that no longer exist. Structured concurrency means child tasks are connected to a parent scope, which makes cancellation and error handling more predictable. When debugging, check which scope launched the task, where exceptions are handled, and whether the result returns after the user has already left the screen.