Kotlin Syntax Cheat Sheet
A lot of services are migrating from Java to Kotlin these days. The first thing I noticed about Kotlin was its conciseness. Being able to express the same logic in less than half the code compared to Java was pretty refreshing. In this post, I'll cover the Kotlin syntax I use most in practice. From Null Safety to coroutines β just the essentials. Variables and Type Inference Kotlin has two ways to declare variables. val is immutable, var is mutable. val name: String = "John" val age = 30 // Type inferred as Int var count: Int = 0 count = 1 const val MAX_SIZE = 100 // Compile-time constant Unlike Java where you had to keep adding final, Kotlin makes val the default so you naturally lean toward immutability. String handling is also much cleaner. With string templates and raw strings, you almost never need to reach for StringBuilder. val json = """ { "name": "$name", "age": $age } """.trimIndent() Null Safety This is one of the biggest reasons to use Kotlin. Since all types are non-null by default, you can catch NullPointerException at compile time. var nickname: String? = null // ? allows null nickname?.length // Safe call: returns null if null nickname!!.length // Non-null assertion: crashes if null val len = nickname?.length ?: 0 // Elvis operator: default value when null In practice, the most common pattern is combining let with the Elvis operator. nickname?.let { println("Nickname: $it") } fun process(name: String?) { val n = name ?: return // Early return if null println(n.uppercase()) } Avoid using the !! operator outside of test code. It's basically reintroducing NPEs. Functions and Extension Functions Default values and named arguments mean problems that required overloading in Java can be solved with a single function. fun greet(name: String = "Guest", age: Int = 0) = "Hello, $name (age $age)" greet(age = 30, name = "John") // Order doesn't matter Extension functions let you add methods to existing classes without modifying them. Especially useful for utility functions. fun String.toSlug() = this.lowercase().replace(" ", "-").replace(Regex("[^a-z0-9-]"), "") "Hello World".toSlug() // "hello-world" Classes: Data Class and Sealed Class Data Class When creating DTOs or model objects, it auto-generates equals(), hashCode(), toString(), and copy(). You can completely eliminate Java boilerplate. data class User( val id: Long, val name: String, val email: String ) val user2 = user1.copy(name = "Jane") // Copy with only some fields changed Sealed Class A class specialized for state management. When used with when expressions, the compiler checks all cases so you don't need an else branch. sealed class Result&x3C;out T> { data class Success&x3C;T>(val data: T) : Result&x3C;T>() data class Error(val message: String) : Result&x3C;Nothing>() object Loading : Result&x3C;Nothing>() } fun handleResult(result: Result&x3C;String>) = when (result) { is Result.Success -> "Success: ${result.data}" is Result.Error -> "Error: ${result.message}" Result.Loading -> "Loading..." } When you add a new state, you'll get compile errors everywhere when is used, so nothing gets missed. Control Flow as Expressions In Kotlin, both if and when are expressions that return values. val max = if (a > b) a else b val message = when (status) { "PENDING" -> "Pending" "CONFIRMED" -> "Confirmed" in listOf("CANCELLED", "FAILED") -> "Failed" else -> "Unknown" } There's no ternary operator (? :) in Java's sense β if itself fills that role. when is far more flexible than switch, supporting range checks and type checks. Functional Collection Processing Kotlin's collection API is more concise than Java Streams. val numbers = listOf(1, 2, 3, 4, 5) numbers.filter { it > 2 } // [3, 4, 5] numbers.map { it * 2 } // [2, 4, 6, 8, 10] numbers.groupBy { it % 2 == 0 } // {false=[1,3,5], true=[2,4]} numbers.chunked(2) // [[1,2], [3,4], [5]] No more .stream().collect(Collectors.toList()). Scope Functions The five scope functions can be confusing at first, but the key is the combination of reference style (it vs this) and return value (lambda result vs the object itself). Function Reference Returns Use Case let it Lambda result Null check, transform run this Lambda result Configure then compute with this Lambda result Multiple method calls apply this Object itself Object initialization also it Object itself Logging, side effects The most common combo in practice is apply + also. val user = User().apply { name = "John" age = 30 }.also { log("User created: $it") } Use apply to initialize the object, then also to handle side effects (like logging). Coroutines Kotlin's async handling is powered by coroutines. The learning curve is lower than RxJava, and the code reads like synchronous code. Basic API Call viewModelScope.launch { _loading.value = true _user.value = repository.getUser(id) _loading.value = false } Parallel Calls A task that takes 3 seconds sequentially can be cut to 1 second with async for parallel processing. coroutineScope { val user = async { fetchUser() } val posts = async { fetchPosts() } val comments = async { fetchComments() } Triple(user.await(), posts.await(), comments.await()) } Thread Switching with Dispatchers viewModelScope.launch { showLoading() // Main thread val user = withContext(Dispatchers.IO) { api.getUser(id) // IO thread } showUser(user) // Back to Main thread } Even with withContext switching threads, you can read top to bottom without callback hell. Java Interop If you need compatibility with existing Java code, use annotations like @JvmStatic and @JvmField. class Utils { companion object { @JvmStatic fun helper() { } } } // Callable from Java as Utils.helper() Wrap-Up Kotlin is ultimately a language that aims for safe and concise code. Among everything covered here, the three things that made the biggest difference in practice were: Null Safety β Elevates runtime crashes to compile-time errors Sealed Class + when β Branch handling with no missed states Coroutines β Write async code without callbacks If you want to go deeper, try practicing with Kotlin Koans. We learn what we have said from those who listen to our speaking.β Kenneth Patton