Kotlin is a statically typed, multi-platform programming language developed by JetBrains that compiles to JVM bytecode, JavaScript, and native code — offering null safety, concise syntax, 100% Java interoperability, and coroutines for asynchronous programming, and is Google’s preferred language for Android development.
📑 Kotlin Reference — All Topics
?, !!, let, also, run — Kotlin's null system.
Data classes, extension functions, apply, scope functions.
suspend, launch, async, Flow for reactive streams.
JetBrains built it to fix Java's pain points — then Google made it Android's language.
kotlinc compiles a single file — but the moment there's a dependency, it's Gradle.
val is the default nearly everyone reaches for — var only when reassignment is real.
No ternary operator exists at all — because if already works as an expression.
Default parameters replace most of what Java ever needed method overloading for.
Every class is final by default — the exact opposite of Java's design.
One keyword generates equals, hashCode, toString, copy, and destructuring at once.
Add a new subclass, and every when missing that case is a compile error.
Add a method to String itself — no inheritance, no touching the original source.
list.filter { it > 5 } is just a trailing lambda moved outside the parens.
List isn't a deep guarantee — it's a read-only VIEW of possibly-mutable data.
out Animal is declared once on the class — not repeated at every call site.
Default method bodies since Kotlin's very first release — Java needed until 8.
No static keyword exists at all — companion object plays that role instead.
The by keyword forwards an entire interface automatically — composition, no boilerplate.
apply and also do almost the same thing — the whole difference is this vs it.
[Wireframe — content coming soon.]
[Wireframe — content coming soon.]
kotlin.test isn't its own framework — it runs on JUnit underneath, on the JVM.
No checked exceptions exist — a deliberate rejection of Java's design.
Anything from Java is a platform type — the one real hole in null safety.
One property compiles to a field, a getter, AND a param — pick which one exactly.
[Wireframe — content coming soon.]
[Wireframe — content coming soon.]
[Wireframe — content coming soon.]
[Wireframe — content coming soon.]
A parent scope genuinely cannot finish until every child has completed.
Kotlin is what Java should have been — it runs on the JVM, is 100% interoperable with Java, but removes the verbosity and null-pointer crashes that made Java painful, and adds modern features like extension functions, data classes, and coroutines.
What Kotlin is
Kotlin is a statically typed, multi-platform programming language developed by JetBrains, with version 1.0 released in February 2016. Kotlin compiles to JVM bytecode (interoperable with all Java libraries), JavaScript, and native machine code (Kotlin/Native). In May 2017, Google announced Kotlin as an officially supported language for Android development, and since 2019 it is the preferred language for Android. Kotlin's design goals: 100% Java interoperability, null safety built into the type system, conciseness (typically 40% less code than equivalent Java), and expressiveness.
fun main() {
// Type inference — no need to declare types explicitly
val name = "Priya" // val = immutable (like Java final)
var count = 0 // var = mutable
val pi = 3.14159
println("Hello, $name!") // string templates — no concatenation
// Null safety built in — types are non-null by default
val city: String = "Mumbai" // cannot be null
val nickname: String? = null // ? = nullable type
// Safe call operator
println(nickname?.length) // null — no NullPointerException
println(nickname?.length ?: "no nickname") // Elvis: fallback value
// When expression (enhanced switch)
val result = when (count) {
0 -> "zero"
1, 2 -> "small"
else -> "large"
}
println(result)
}Null safety — eliminating NullPointerException
Null safety is Kotlin's defining feature. Every type is non-null by default — String can never be null. To allow null, append ?: String?. The compiler enforces safe access: you cannot call methods on a nullable type without handling the null case first. This eliminates the NullPointerException — the "billion dollar mistake" — at compile time.
fun findUser(id: Int): String? {
return if (id == 1) "Priya" else null
}
fun main() {
val user = findUser(99)
// Safe call — returns null instead of throwing
println(user?.length) // null
// Elvis operator — provide a default
val name = user ?: "Anonymous" // "Anonymous"
// Non-null assertion — throws KotlinNullPointerException if null
// Only use when you are CERTAIN it is not null
// val len = user!!.length // dangerous
// Smart cast — after null check, compiler knows type
if (user != null) {
println(user.length) // OK — compiler narrows to String
}
// let — run block only if non-null
user?.let { u ->
println("Found: $u (${u.length} chars)")
}
}Data classes
A data class automatically generates equals(), hashCode(), toString(), copy(), and component functions for destructuring. This replaces hundreds of lines of Java boilerplate for plain data-holding classes.
// One line replaces ~50 lines of Java
data class User(
val id: Int,
val name: String,
val email: String,
val active: Boolean = true // default parameter
)
fun main() {
val user1 = User(1, "Priya", "priya@example.com")
val user2 = user1.copy(email = "new@example.com") // changed copy
println(user1) // User(id=1, name=Priya, email=priya@example.com, active=true)
println(user1 == user2) // false — structural equality
println(user1 === user2) // false — reference equality
// Destructuring
val (id, name, email) = user1
println("$id: $name")
}Extension functions and properties
Extension functions let you add methods to existing classes without inheriting from them or modifying their source code. They are resolved statically (at compile time), not dynamically — they do not actually modify the class.
// Add a method to String without touching the String class
fun String.isPalindrome(): Boolean {
return this == this.reversed()
}
// Extension property
val String.wordCount: Int
get() = if (isBlank()) 0 else trim().split("\s+".toRegex()).size
fun main() {
println("racecar".isPalindrome()) // true
println("hello world".wordCount) // 2
println(" hello world ".wordCount) // 2
// Standard library uses extensions extensively
val nums = listOf(1, 2, 3, 4, 5)
println(nums.filter { it % 2 == 0 }) // [2, 4]
println(nums.map { it * it }) // [1, 4, 9, 16, 25]
println(nums.sumOf { it }) // 15
}Collections and functional operations
Kotlin's collection API is built on extension functions. Collections are immutable by default (listOf, mapOf, setOf). Mutable variants: mutableListOf, mutableMapOf. Kotlin provides a rich set of higher-order functions: map, filter, reduce, fold, flatMap, groupBy, sortedBy, distinct, zip, partition.
Sealed classes and exhaustive when
A sealed class restricts which classes can inherit from it — all subclasses must be in the same package/file. Combined with when, this creates exhaustive pattern matching: the compiler verifies every subclass is handled, eliminating the need for an else branch and catching missing cases at compile time. This is Kotlin's equivalent of Rust's enums or Haskell's algebraic data types.
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val message: String, val code: Int) : Result<Nothing>()
object Loading : Result<Nothing>()
}
fun handleResult(result: Result<String>) {
// Compiler verifies all cases are handled — no else needed
when (result) {
is Result.Success -> println("Got: ${result.data}")
is Result.Error -> println("Error ${result.code}: ${result.message}")
Result.Loading -> println("Loading...")
}
}
fun main() {
handleResult(Result.Success("Hello"))
handleResult(Result.Error("Not found", 404))
handleResult(Result.Loading)
}Coroutines — structured concurrency
Kotlin coroutines are the standard way to write asynchronous code in Kotlin. A coroutine is a suspendable computation — it can be suspended (paused) without blocking a thread, and resumed later. Coroutines are not threads: millions of coroutines can coexist on a handful of threads. The suspend keyword marks a function that can be suspended. Coroutines use structured concurrency: a coroutine is always launched in a CoroutineScope, and if the scope is cancelled, all child coroutines are cancelled too — preventing coroutine leaks.
import kotlinx.coroutines.*
suspend fun fetchUser(id: Int): String {
delay(100) // non-blocking delay — suspends coroutine, not thread
return "User$id"
}
fun main() = runBlocking { // creates coroutine scope for main
// Launch concurrent coroutines
val job1 = async { fetchUser(1) }
val job2 = async { fetchUser(2) }
// Await both results — runs in parallel
println("${job1.await()} and ${job2.await()}")
// Structured: launch N coroutines and wait for all
val results = (1..5).map { id ->
async { fetchUser(id) }
}.awaitAll()
println(results)
// Flow — cold asynchronous stream (like Rx)
// flow { emit(1); emit(2); emit(3) }
// .filter { it > 1 }
// .collect { println(it) }
}Kotlin vs Java: key differences
| Feature | Java | Kotlin |
|---|---|---|
| Null safety | No — NPE at runtime | Yes — compiler enforces non-null by default |
| Data classes | ~50 lines of boilerplate | data class User(val name: String) |
| Extension functions | No | Yes — add methods to any class |
| Default parameters | No — method overloading | Yes — fun f(x: Int = 0) |
| Smart cast | Explicit cast needed after instanceof | Auto-cast after is-check |
| String templates | "Hello " + name | "Hello $name" |
| Functional collections | Java Streams API | Built-in extension functions |
| Coroutines | No (Project Loom is similar) | First-class via kotlinx.coroutines |
| Immutability | final keyword | val (immutable) vs var (mutable) |
Kotlin Multiplatform
Kotlin Multiplatform (KMP) allows sharing business logic code across platforms — Android, iOS, web (via Kotlin/JS), desktop, and server — while using native UI frameworks on each platform. KMP compiles Kotlin to JVM bytecode (Android/JVM), JavaScript (web), and LLVM-based native code (iOS, macOS, Linux). This is distinct from Flutter/React Native which render UI uniformly; KMP shares only logic, not UI. JetBrains declared KMP stable in November 2023.
val does not mean the object is immutable — it means the reference cannot be reassigned. val list = mutableListOf(1,2,3): list is val, but you can still call list.add(4). The list reference is fixed; the list contents are not. For a truly immutable list, use listOf().launch { } creates a coroutine, not a thread. Thousands of coroutines can run on a single thread by suspending and resuming. Coroutines use thread pools (Dispatchers.IO, Dispatchers.Default) managed by the runtime — you do not create OS threads manually.Kotlin's type system: nullability and generics
Kotlin's type system distinguishes nullable (T?) and non-nullable (T) types at the type level. The compiler tracks nullability through the control flow graph — after a null check, the type is narrowed (smart cast). Kotlin's generics are similar to Java's but add explicit variance annotations: out T (covariant — producer), in T (contravariant — consumer). This resolves the use-site variance verbosity of Java (? extends T, ? super T) by declaring variance at the declaration site. Nothing is Kotlin's bottom type — a value of type Nothing never exists; functions that always throw or loop forever return Nothing.
Coroutines implementation: continuation-passing style
Kotlin coroutines are implemented via continuation-passing style (CPS) transformation at compile time. A suspend fun is transformed by the compiler into a state machine with a hidden Continuation<T> parameter. Each suspension point becomes a state in the machine. When suspended, the current state is saved in the Continuation object (heap-allocated) and the thread is released. When resumed, the state machine continues from where it left off. This is why millions of coroutines can exist with minimal overhead — each coroutine stores only its current state, not an entire OS thread stack.
Inline functions and reified generics
Kotlin inline functions have their body inlined at call sites — eliminating the overhead of lambda object creation and function call. This is critical for higher-order functions used in hot loops: filter, map, forEach are all inline. reified type parameters (only available with inline) allow accessing the actual runtime type of a generic parameter — solving the type erasure problem. inline fun <reified T> instanceOf(value: Any) = value is T works because the compiler inlines the function and substitutes the actual type for T.
JetBrains. Kotlin Language Specification. kotlinlang.org/spec/. Kotlin documentation: kotlinlang.org/docs/. Kotlin coroutines guide: kotlinlang.org/docs/coroutines-guide.html. JetBrains. "Kotlin Multiplatform Stable." blog.jetbrains.com, November 2023.