The When Expression
When replaces switch
Kotlin's when expression replaces Java's switch statement with a more powerful construct. It can match on values, types, ranges, and arbitrary predicates.
Basic Value Matching
val day = 3
val dayName = when (day) {
1 -> "Monday"
2 -> "Tuesday"
3 -> "Wednesday"
4 -> "Thursday"
5 -> "Friday"
6 -> "Saturday"
7 -> "Sunday"
else -> "Invalid"
}
println(dayName) // Wednesday
When used as an expression, it returns a value. The else branch is required when no branch matches.
Multiple Values and Ranges
Group values with commas. Check membership with in:
val score = 85
val grade = when (score) {
in 90..100 -> "A"
in 80 until 90 -> "B"
in 70 until 80 -> "C"
in 60 until 70 -> "D"
else -> "F"
}
The in operator checks if a value falls within a range. until creates a half-open range (excludes the upper bound).
Type Checking
Use is to match on types. The compiler smart-casts the variable inside the branch:
fun describe(obj: Any): String {
return when (obj) {
is Int -> "Integer: $obj"
is String -> "String of length ${obj.length}"
is List<*> -> "List of ${obj.size} elements"
else -> "Unknown type"
}
}
The smart cast means obj is automatically treated as String inside the is String branch, so you can call .length without an explicit cast.
Without Argument
When used without an argument, each branch is a boolean condition. The first matching branch executes:
val temperature = 28
when {
temperature >= 35 -> println("Extreme heat")
temperature >= 25 -> println("Warm")
temperature >= 15 -> println("Mild")
else -> println("Cold")
}
This replaces chains of if-else-if with clearer syntax.
When as Statement
When can be used as a statement (no value returned) for side effects:
when (action) {
"save" -> saveData()
"delete" -> deleteData()
"load" -> loadData()
else -> println("Unknown action")
}
For Loops and Ranges
Ranges
Ranges in Kotlin are created with .. (inclusive) and until (exclusive). They implement the Iterable interface and work with for loops, in, and standard library functions:
// Inclusive range: 1, 2, 3, 4, 5
for (i in 1..5) {
print("$i ")
}
// Exclusive range: 1, 2, 3, 4
for (i in 1 until 5) {
print("$i ")
}
// Downward range: 5, 4, 3, 2, 1
for (i in 5 downTo 1) {
print("$i ")
}
// Step: 1, 3, 5, 7, 9
for (i in 1..10 step 2) {
print("$i ")
}
Iterating Collections
val fruits = listOf("apple", "banana", "cherry")
for (fruit in fruits) {
println(fruit)
}
// With index
for ((index, fruit) in fruits.withIndex()) {
println("$index: $fruit")
}
Destructuring in For Loops
Kotlin supports destructuring declarations in for loops. Any object with componentN() functions (including data classes and Map entries) can be destructured:
val capitals = mapOf(
"USA" to "Washington",
"Japan" to "Tokyo",
"Germany" to "Berlin"
)
for ((country, city) in capitals) {
println("$country -> $city")
}
// Data class destructuring
data class User(val name: String, val age: Int)
val users = listOf(User("Alice", 30), User("Bob", 25))
for ((name, age) in users) {
println("$name is $age years old")
}
Break and Continue
Kotlin supports break and continue with labels for nested loops:
outer@ for (i in 1..5) {
for (j in 1..5) {
if (j == 3) continue@outer // Skips to next i
if (i == 4) break@outer // Exits outer loop
print("($i,$j) ")
}
}
// Output: (1,1) (1,2) (2,1) (2,2) (3,1) (3,2)
Without labels, break and continue affect only the innermost loop.
If as an Expression
If Returns a Value
In Kotlin, if is an expression, not a statement. It returns a value, so you can use it where other languages require ternary operators:
val max = if (a > b) a else b
// With blocks
val result = if (score >= 90) {
println("Excellent!")
"A"
} else if (score >= 80) {
"B"
} else {
"C"
}
The last expression in each branch is the return value. This eliminates the need for ternary operators (which Kotlin does not have).
If vs When
Use when for three or more branches or pattern matching. Use if-else for simple two-branch decisions:
// Simple: if-else is fine
val abs = if (x >= 0) x else -x
// Complex: when is clearer
val description = when {
x > 0 -> "positive"
x < 0 -> "negative"
else -> "zero"
}
Elvis with If
The Elvis operator often replaces simple if-null checks:
// Verbose
val length = if (name != null) name.length else 0
// Concise
val length = name?.length ?: 0
The Elvis operator is preferred because it is more concise and signals null handling intent clearly.
Practice Problems
Write a function that prints numbers 1 to 100. For multiples of 3 print 'Fizz', for multiples of 5 print 'Buzz', for multiples of both print 'FizzBuzz', otherwise print the number. Use when expression.
Solution
fun fizzBuzz() {
for (i in 1..100) {
val result = when {
i % 15 == 0 -> "FizzBuzz"
i % 3 == 0 -> "Fizz"
i % 5 == 0 -> "Buzz"
else -> i.toString()
}
println(result)
}
} Write a function that takes a score (0-100) and returns the letter grade using ranges: A (90-100), B (80-89), C (70-79), D (60-69), F (below 60). Handle invalid scores.
Solution
fun getGrade(score: Int): String {
return when (score) {
in 90..100 -> "A"
in 80 until 90 -> "B"
in 70 until 80 -> "C"
in 60 until 70 -> "D"
in 0 until 60 -> "F"
else -> "Invalid score"
}
}
fun main() {
println(getGrade(95)) // A
println(getGrade(82)) // B
println(getGrade(67)) // D
println(getGrade(45)) // F
println(getGrade(105)) // Invalid score
} Given a map of product names to prices, iterate using destructuring and print each product. Then calculate the total price of all products priced above 50.
Solution
fun processProducts(products: Map<String, Double>) {
for ((name, price) in products) {
println("$name: $${price}")
}
val totalExpensive = products.values
.filter { it > 50.0 }
.sum()
println("Total above $50: $${totalExpensive}")
}
fun main() {
val products = mapOf(
"Laptop" to 999.99,
"Mouse" to 25.50,
"Keyboard" to 75.00,
"Monitor" to 350.00
)
processProducts(products)
} Quiz
1. What makes Kotlin's when different from Java's switch?
2. What does `1 until 5` produce?
3. What does this code print? ```kotlin val x = 10 val result = if (x > 5) "big" else "small" println(result) ```
4. How do you exit a nested for loop in Kotlin?
Flashcards
Question
What is the difference between .. and until in Kotlin ranges?
Click to reveal answer
Answer
.. creates an inclusive range (includes both endpoints). until creates a half-open range (excludes the upper bound). Example: 1..5 = 1,2,3,4,5; 1 until 5 = 1,2,3,4.
Question
How does smart casting work in when expressions?
Click to reveal answer
Answer
When you use is to check a type, the compiler automatically casts the variable to that type within the branch. No explicit as cast needed. Example: is String lets you call .length directly.
Question
What is a labeled break in Kotlin?
Click to reveal answer
Answer
A break with a label (break@outerName) exits a specific outer loop instead of just the innermost loop. Labels are defined with labelName@ before the loop.
Question
Why does Kotlin not have a ternary operator?
Click to reveal answer
Answer
Because if is already an expression that returns a value. if (condition) valueA else valueB serves the same purpose without needing a separate operator.
Revision Notes
Key Takeaways
- 1. when is more powerful than switch: it matches values, types, ranges, and conditions.
- 2. if is an expression in Kotlin and returns a value.
- 3. Ranges with .. are inclusive; until is exclusive.
- 4. Destructuring in for loops makes Map iteration clean.
- 5. Labeled break/continue control nested loops.
Interview Tips
- • Be ready to write when expressions for pattern matching problems.
- • Know the difference between .. (inclusive) and until (exclusive).
- • Practice destructuring data classes and Map entries in loops.
- • Explain why if-as-expression eliminates the need for ternary operators.
Cheat Sheet
Control Flow Cheat Sheet
When Expression:
- Matches values:
when (x) { 1 -> ... } - Matches types:
when (x) { is String -> ... } - Matches ranges:
when (x) { in 1..10 -> ... } - Boolean conditions:
when { x > 0 -> ... } - Returns a value when used as expression
For Loops:
for (i in 1..5)— inclusivefor (i in 1 until 5)— exclusivefor (i in 5 downTo 1)— descendingfor (i in 1..10 step 2)— with stepfor ((k, v) in map)— destructuring
If as Expression:
val x = if (cond) a else b- Returns the last expression in each branch
- Replaces ternary operator
Labels:
outer@ for (...) { break@outer }