🌀

A language tour

The Modernity of Kotlin

Built by toolmakers at JetBrains — a language that fixes Java's papercuts while keeping everything that made Java work.

scroll

01 — Null Safety

Safety by design

Kotlin eliminates the billion-dollar mistake of null references. Types are non-nullable by default — null becomes a conscious choice, marked explicitly, handled at the point where it might occur.

"I call it my billion-dollar mistake. It was the invention of the null reference in 1960."

— Sir Tony Hoare, inventor of null references
null_safety.kt
// Non-nullable by default
var name: String = "Kotlin"  // Cannot be null
// name = null  // Compile error!

// Explicit nullable types
var maybeName: String? = "Hello"
maybeName = null  // This is allowed

// Safe calls — no more NPEs
val length = maybeName?.length  // Returns null if maybeName is null

// Elvis operator — provide defaults
val displayName = maybeName ?: "Unknown"  // "Unknown" if null

// Non-null assertion (use carefully!)
val sureName = maybeName!!  // Throws NPE if null

The ?. safe call returns null instead of crashing — and the compiler forces you to handle that possibility before it reaches production.


02 — Conciseness

Less code, more meaning

Kotlin cuts the boilerplate. Data classes, type inference, and smart defaults let you focus on what matters.

"A data class that takes 50 lines in Java takes one in Kotlin. That's not a shortcut — it's a reappraisal of what the programmer's job actually is."

— The Kotlin design philosophy
concise.kt
// Data class — getters, setters, equals, hashCode, toString for free
data class User(val name: String, val age: Int)

val user = User("Alice", 30)
println(user)  // User(name=Alice, age=30)

// Type inference
val message = "Hello"  // String inferred
val numbers = listOf(1, 2, 3)  // List<Int> inferred

// When expressions replace switch
fun describe(obj: Any): String =
    when (obj) {
        1 -> "One"
        "Hello" -> "Greeting"
        is Long -> "Long number"
        else -> "Unknown"
    }

// Single-expression functions
fun double(x: Int) = x * 2

A data class generates equals, hashCode, toString, copy, and destructuring declarations — all from one line.


03 — Functional Programming

Functions as first-class citizens

Kotlin embraces functional programming with lambdas, higher-order functions, and immutability — but keeps it practical.

functional.kt
// Lambdas and higher-order functions
val numbers = listOf(1, 2, 3, 4, 5)

val doubled = numbers.map { it * 2 }  // [2, 4, 6, 8, 10]
val evens = numbers.filter { it % 2 == 0 }  // [2, 4]
val sum = numbers.fold(0) { acc, n -> acc + n }  // 15

// Function types
val operation: (Int, Int) -> Int = { a, b -> a + b }
val result = operation(3, 4)  // 7

// Inline functions for performance
inline fun measureTime(block: () -> Unit) {
    val start = System.currentTimeMillis()
    block()
    val end = System.currentTimeMillis()
    println("Time: ${end - start}ms")
}

measureTime {
    println("Doing work...")
}

it is the implicit parameter for single-parameter lambdas — less typing, same meaning.


04 — Interoperability

Seamless Java integration

Kotlin compiles to JVM bytecode and calls Java code naturally. No wrappers, no impedance mismatch — just works.

interop.kt
// Call Java code directly
import java.util.*

val list = ArrayList<String>()  // Java ArrayList
list.add("Kotlin")
list.add("Java")

// Kotlin collections to Java
val kotlinList = listOf("A", "B", "C")
val javaList: List<String> = kotlinList

// Extension functions on Java classes
fun String.isPalindrome(): Boolean =
    this == this.reversed()

println("racecar".isPalindrome())  // true

// Java methods that return null are safely wrapped
val homePath: String? = System.getProperty("user.home")  // Returns null if unset
val length = homePath?.length ?: 0

Extension functions let Kotlin add methods to Java's String, List, and any other class — without subclassing, without wrappers.


05 — Coroutines

Async without the complexity

Kotlin's coroutines make asynchronous programming feel like synchronous code. No callbacks, no promises — just sequential logic.

coroutines.kt
import kotlinx.coroutines.*

suspend fun fetchUser(): String {
    delay(1000L)  // Non-blocking pause — no thread is held
    return "Alice"
}

suspend fun fetchPosts(): List<String> {
    delay(800L)
    return listOf("Post 1", "Post 2")
}

fun main() = runBlocking {
    // Sequential: 1800ms total — each waits for the previous
    val user1 = fetchUser()
    val posts1 = fetchPosts()

    // Concurrent: 1000ms total — both run in parallel
    val user = async { fetchUser() }
    val posts = async { fetchPosts() }

    println("User: ${user.await()}")
    println("Posts: ${posts.await()}")
}

async { } launches two coroutines simultaneously — await() collects them. Both network calls overlap, with no threads blocked during either delay.


06 — The Whole Picture

More reasons to love it

🏗️

Sealed Classes

Algebraic data types for exhaustive when expressions — the compiler ensures you handle all cases.

🔄

Smart Casts

After is checks, types are automatically cast — no explicit casting needed.

📱

Multiplatform

One language for JVM, JavaScript, and native — share code across platforms.

Inline Classes

Zero-cost abstractions for types like meters vs feet — type safety without runtime overhead.

🎯

Operator Overloading

Define +, -, etc. for your types — make domain-specific operators.

🚀

Android First

In 2017, Google endorsed Kotlin for Android. JetBrains had spent six years making the case that language quality matters.