Understanding Kotlin Control Flow
1. Using If Expressions
| Feature | Description | Example |
|---|---|---|
| If Expression | Used for conditional logic, evaluates to a value. | |
| If-Else Chain | Allows multiple conditions in sequence. | |
| If Expression with Multiple Statements | Use curly braces to group multiple statements. | |
Example: Nested If Expressions
val x = 10
val y = 20
val result = if (x > 5) {
if (y > 15) "Both conditions met"
else "Only x condition met"
} else {
"No conditions met"
}
2. Working with When Expressions
The when expression in Kotlin is a powerful control flow construct that serves as a replacement for switch statements in other languages. It evaluates an expression and matches the result against multiple branches.
| Feature | Description | Example |
|---|---|---|
| Basic Syntax | Matches a value against multiple conditions | |
| Type Checking | Can check types using is |
|
| Condition Matching | Supports complex conditions with if |
|
| Return Value | Can be used as an expression returning a value | |
Example: Using When with String Values
fun getDayType(day: String): String {
return when (day) {
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> "Weekday"
"Saturday", "Sunday" -> "Weekend"
else -> "Invalid day"
}
}
println(getDayType("Monday")) // Output: Weekday
Example: When with Type Checks
fun processValue(value: Any): String {
return when (value) {
is Int -> "Integer: $value"
is String -> "String: $value"
is Double -> "Double: $value"
else -> "Unknown type"
}
}
println(processValue(42)) // Output: Integer: 42
switch in Java, Kotlin's when does not fall through to the next case. Each branch is evaluated independently.
else, the when expression will not compile if it's expected to return a value.
3. Implementing For Loops
| Loop Type | Description | Example |
|---|---|---|
| for (item in collection) | Iterates over items in a collection | |
| for (index in range) | Iterates over a numeric range | |
| for (index in collection.indices) | Iterates over indices of a collection | |
Example: Looping with Index
val fruits = listOf("Apple", "Banana", "Cherry")
for ((index, fruit) in fruits.withIndex()) {
println("Fruit $index is $fruit")
}
withIndex() function is useful for accessing both index and value during iteration.
ConcurrentModificationException.
4. Using While and Do-While Loops
| Loop Type | Syntax | Description | Use Case |
|---|---|---|---|
| while | while (condition) { ... } |
Evaluates condition before each iteration. Loop may not execute at all. | When the number of iterations is unknown but depends on a condition. |
| do-while | do { ... } while (condition) |
Executes the loop body at least once, then evaluates condition. | When you want to execute code at least once before checking the condition. |
while loops to avoid infinite loops. Always ensure the loop condition will eventually become false.
Key Differences
- While Loop: Checks condition before executing the loop body.
- Do-While Loop: Executes the loop body at least once, then checks the condition.
5. Applying Break and Continue
| Control Flow | Description | Usage |
|---|---|---|
| break | Exits the innermost loop or switch statement | for (i in 1..5) { if (i == 3) break; println(i) } |
| continue | Skips the current iteration and continues with the next one | for (i in 1..5) { if (i == 3) continue; println(i) } |
Example: Using break in nested loops
outer@ for (i in 1..3) {
for (j in 1..3) {
if (j == 2) break@outer
println("i = $i, j = $j")
}
}
Example: Using continue in nested loops
outer@ for (i in 1..3) {
for (j in 1..3) {
if (j == 2) continue@outer
println("i = $i, j = $j")
}
}
6. Using Return with Labels (@label)
In Kotlin, return statements can be used with labels to control the flow of nested loops or functions. Labels allow you to specify which loop or function you want to return from, especially useful in nested structures.
| Feature | Description | Example |
|---|---|---|
| Label Syntax | Labels are defined using the @ symbol before a name. |
|
| Return with Label | Use return@label to return from a specific labeled block. |
|
Example: Using Labels to Break Nested Loops
fun findFirstPositive(): Int? {
outerLoop@ for (i in 1..3) {
for (j in 1..3) {
if (i * j > 5) {
break@outerLoop
}
println("i=$i, j=$j")
}
}
return null
}
Example: Using Labels with Return
fun processMatrix(matrix: Array<Array<Int>>): Int {
outer@ for (i in matrix.indices) {
for (j in matrix[i].indices) {
if (matrix[i][j] < 0) {
return@outer -1
}
}
}
return 0
}
7. Implementing Try-Catch as Expression
| Construct | Description | Usage |
|---|---|---|
| try-catch as expression | Allows try-catch blocks to be used as expressions, returning a value. |
|
Example: Try-Catch Expression
fun parseStringToInt(input: String): Int {
return try {
input.toInt()
} catch (e: NumberFormatException) {
0
}
}
val result = parseStringToInt("123") // Returns 123
val fallback = parseStringToInt("abc") // Returns 0
try-catch block must have a return type that matches the expected value, and all branches must return the same type.8. Using Safe Calls and Elvis Operator (?., ?:)
The safe call operator (?) and the Elvis operator (?:) are essential Kotlin features for handling null values safely.
| Operator | Description | Example | Result |
|---|---|---|---|
| ? | Safe call operator. Calls a method or accesses a property only if the receiver is not null. | val name = person?.name |
If person is null, name will be null. Otherwise, it will hold the value of person.name. |
| ?: | Elvis operator. Provides a default value if the expression on its left is null. | val name = person?.name ?: "Unknown" |
If person is null or name is null, name will be "Unknown". |
Example: Safe Calls in a Function
fun getPersonName(person: Person?): String {
return person?.name ?: "No Name"
}
// Usage
val person: Person? = null
println(getPersonName(person)) // Output: No Name
Example: Chaining Safe Calls
val address = person?.address?.street ?: "No Address"
// Equivalent to:
// val address = if (person != null && person.address != null) {
// person.address.street
// } else {
// "No Address"
// }
9. Working with Not-Null Assertion (!!)
The not-null assertion operator (!!) is used to explicitly assert that a value is not null in Kotlin. It forces the compiler to treat a nullable type as non-nullable, and will throw a NullPointerException at runtime if the value is indeed null.
| Operator | Description | Example |
|---|---|---|
| !! | Asserts that the value is not null and throws an exception if it is | |
Example: Using !! to Force Non-Null Value
val name: String? = "Kotlin"
println(name!!.length) // Outputs: 5
Example: Runtime Exception with !!
val nullableValue: Int? = null
val result = nullableValue!! // Throws NullPointerException
!! should be avoided in most cases as it can lead to runtime exceptions. Prefer safe calls (?) or if checks for safer handling.
10. Implementing Smart Casts
Smart casts in Kotlin allow you to safely cast a variable to a more specific type without explicitly checking or casting it. The compiler automatically handles the type check and cast when it can determine that a variable is of a specific type at runtime.
| Condition | Description | Example |
|---|---|---|
Type check with is |
Checks if a variable is of a specific type and enables smart cast | |
Type check with as |
Explicit cast, but only if type is confirmed | |
Safe cast with as? |
Safely casts and returns null if cast fails | |
Example: Smart Cast in Conditional
val value: Any = "Kotlin"
if (value is String) {
println("Length: ${value.length}") // Smart cast to String
}
Example: Using when with Smart Casts
val items: List<Any> = listOf("Hello", 42, true)
items.forEach {
when (it) {
is String -> println("String: $it")
is Int -> println("Int: $it")
is Boolean -> println("Boolean: $it")
}
}