Kotlin
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Null Safety
-
Section 4: Object-Oriented Kotlin
-
Section 5: Functional Kotlin
-
Section 6: Coroutines
-
Section 7: Collections Deep Dive
-
Section 8: Type System Deep Dive
-
Section 9: Interop and Tooling
-
Section 10: Kotlin DSLs and Patterns
-
Section 11: Testing and Quality
-
Section 12: Server-Side Kotlin
-
Section 13: Practical Projects
-
Section 14: Interview Practice
-
Section 15: More Practice Exercises
-
Section 16: More Standard Library
-
Section 17: Multiplatform Kotlin
-
Section 18: kotlin.collections In Depth
-
Section 19: kotlin.text In Depth
-
Section 20: kotlin.ranges and kotlin.sequences
-
Section 21: kotlin.io and File Handling
-
Section 22: kotlinx.coroutines Deep Dive
-
Section 23: kotlin.reflect
-
Section 24: Android Development with Kotlin Overview
-
Section 25: Kotlin Multiplatform Deep Dive
-
Section 26: Kotlin for Backend Deep Dive
-
Section 27: Kotlin Design Patterns
-
Section 28: Advanced Language Features
-
Section 29: More Practice Exercises
-
Section 30: More Interview Practice
-
Section 31: Kotlin Type System Deep Dive
-
Section 32: Kotlin Null Safety Advanced
-
Section 33: Kotlin Testing Deep Dive
-
Section 34: Kotlin Build Tooling Deep Dive
-
Section 35: Kotlin Serialization
-
Section 36: Kotlin Performance Considerations
-
Section 37: Kotlin Native Overview
-
Section 38: Kotlin for Data and Scripting
-
Section 39: More Coroutines Practice
-
Section 40: More Android-Adjacent Patterns
-
Section 41: More Practical Projects
-
Section 42: More Design and Architecture Practice
-
Section 43: Kotlin Language Evolution
-
Section 44: More Interview and Review
-
Section 45: Kotlin Delegation Patterns Deep Dive
-
Section 46: Kotlin Annotations Deep Dive
-
Section 47: Kotlin for Gradle Plugin Development
-
Section 48: Kotlin Concurrency Beyond Coroutines
-
Section 49: Kotlin Compiler Internals
-
Section 50: Real-World Kotlin Case Studies
-
Section 51: Final Practice Projects
-
Section 52: Kotlin for Server-Side Reactive Programming
-
Section 53: More Practice and Drills
-
Section 54: Kotlin Security Practices
6: Control Flow Statements
I've spent a lot of time reviewing code from developers moving to Kotlin from Java or C#, and there is one specific pattern that almost everyone trips over in their first week. It usually looks something like this:
enum class OrderStatus { PENDING, SHIPPED, DELIVERED, CANCELLED }
fun getStatusMessage(status: OrderStatus): String {
return when (status) {
OrderStatus.PENDING -> "Your order is being prepared."
OrderStatus.SHIPPED -> "Your order is on its way!"
OrderStatus.DELIVERED -> "Package arrived!"
}
}
If you try to compile this, the IDE is going to scream at you. You'll see an error saying 'when' expression must be exhaustive. The developer who wrote this probably thought, "I handled the three main states, the cancelled ones don't need a message," or they simply forgot about the CANCELLED state. In other languages, a switch statement might just do nothing if no case matches. Kotlin is different.
The Exhaustiveness Requirement
When you use when as an expression—meaning you are returning its result or assigning it to a variable—Kotlin forces you to cover every single possible outcome. It’s a safety feature. It prevents that nightmare scenario where you add a new status to your enum six months from now, forget to update this function, and your app crashes or returns null in production.
To fix this, you have two choices. You can explicitly handle the missing case, or use an else block as a catch-all:
fun getStatusMessage(status: OrderStatus): String {
return when (status) {
OrderStatus.PENDING -> "Your order is being prepared."
OrderStatus.SHIPPED -> "Your order is on its way!"
OrderStatus.DELIVERED -> "Package arrived!"
OrderStatus.CANCELLED -> "Your order was cancelled."
// Or just use: else -> "Unknown status"
}
}
Treating 'if' as a Value
One thing I love about Kotlin is that almost everything is an expression. In many languages, if is a statement—it performs an action. In Kotlin, if can return a value. You'll see a lot of legacy-style code that looks like this:
var message: String
if (isLoggedIn) {
message = "Welcome back!"
} else {
message = "Please log in."
}
That's way too much boilerplate. Since if returns a value, you can assign it directly to a val. I highly recommend doing this whenever possible because it lets you keep your variables immutable, which makes your code much easier to reason about:
val message = if (isLoggedIn) "Welcome back!" else "Please log in."
The Versatility of 'when'
Beyond just replacing switch statements, when is incredibly flexible. You aren't limited to checking a single variable against constants. You can check types, ranges, or even use it without an argument at all to replace a long chain of if-else if blocks.
Check this out:
fun describeScore(score: Int) = when {
score >= 90 -> "A"
score >= 80 -> "B"
score in 70..79 -> "C"
else -> "F"
}
Notice how I used in 70..79? That's a range check. It's clean, readable, and eliminates the need for those clunky && operators.
Iterating with Ranges and Steps
For loops in Kotlin are fundamentally different from the for(int i=0; i<10; i++) syntax you might be used to. We iterate over things: ranges, collections, or arrays.
- The Inclusive Range:
for (i in 1..5)runs from 1 to 5. - The Exclusive Range:
for (i in 1 until 5)runs from 1 to 4. This is the one you'll use most often when iterating through indices of a list. - Counting Down:
for (i in 5 downTo 1)does exactly what it says. - Custom Steps:
for (i in 1..10 step 2)will give you 1, 3, 5, 7, 9.
I usually suggest using until for list indices to avoid the dreaded IndexOutOfBoundsException.
While and Do-While Loops
While loops in Kotlin are pretty standard. The while loop checks the condition first; if it's false, the block never runs. The do-while loop ensures the block runs at least once before checking the condition. You won't use these as often as for loops or when expressions, but they're essential for scenarios where you don't know exactly how many iterations you need—like polling an API until a certain status is reached.
📋 Practical Task
Build a Tiered Shipping Calculator
You are building a shipping module for an e-commerce app. Your task is to create a function called calculateShippingCost that determines the cost of shipping based on two factors: the orderValue (Double) and the membershipLevel (String: "GOLD", "SILVER", or "BRONZE").
Implement the following logic using when and if expressions:
- GOLD members: Always get free shipping (0.0), regardless of order value.
- SILVER members: Free shipping if the order value is over $50.0, otherwise they pay $5.0.
- BRONZE members: Free shipping if the order value is over $100.0, otherwise they pay $10.0.
- Unknown membership: If the membership string doesn't match the three levels above, charge a flat rate of $15.0.
Requirements:
- Use a
whenexpression to handle the membership levels. - Use an
ifexpression inside the SILVER and BRONZE branches to handle the value threshold. - Ensure the function returns a
Double.
There are no comments for now.