Skip to content

Kotlin Control Flow — If, When, and Loops Guide

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about Kotlin Control Flow. We cover key concepts, practical examples, and best practices to help you master this topic.

Kotlin control flow uses if and when for branching and for, while, and do-while loops for iteration, with ranges providing concise syntax for sequences of values.

What You'll Learn

  • Use if as both a statement and an expression
  • Replace switch with the powerful when expression
  • Iterate with for loops over ranges, collections, and maps
  • Use while and do-while for conditional iteration
  • Work with ranges (.., until, downTo, step)
  • Break and continue with labels
  • Write idiomatic control flow patterns

Why It Matters

Control flow determines how your program makes decisions and repeats operations. Kotlin's when expression is more powerful than Java's switch — it supports arbitrary conditions, ranges, and type checks without fall-through bugs. Ranges make loops more readable than traditional for loops with index variables. Mastering these constructs lets you write code that is both concise and expressive.

Real-World Use

DodaTech uses when expressions extensively for handling API response states (loading, success, error, empty) in its Android utility app. The exhaustive when ensures all states are handled at compile time. For loops over ranges drive pagination logic in data synchronization services.

Learning Path

flowchart LR
  A[Kotlin Basics] --> B[Control Flow\nYou are here]
  B --> C[Functions]
  style B fill:#f90,color:#fff

The if Expression

In Kotlin, if is an expression that returns a value, not just a statement.

fun main() {
    val a = 15
    val b = 20
    
    // if as an expression
    val max = if (a > b) a else b
    println("Max: $max")  // Output: Max: 20
    
    // if with multiple branches
    val grade = 85
    val result = if (grade >= 90) {
        "Excellent"
    } else if (grade >= 70) {
        "Good"
    } else if (grade >= 50) {
        "Pass"
    } else {
        "Fail"
    }
    println("Result: $result")  // Output: Result: Good
    
    // if with side effects
    if (grade in 70..100) {
        println("You passed with flying colors!")
    }
}

Output: The max variable holds the larger value. The result variable holds "Good" for a grade of 85.

The last expression in each if branch becomes the return value. No ternary operator is needed because if already works as a ternary.

The when Expression

The when expression replaces Java's switch statement and is far more flexible.

fun describe(obj: Any): String = when (obj) {
    1 -> "One"
    "Hello" -> "Greeting"
    is Long -> "Long number"
    !is String -> "Not a string"
    in 1..10 -> "Between 1 and 10"
    else -> "Unknown"
}

fun main() {
    println(describe(1))              // Output: One
    println(describe("Hello"))        // Output: Greeting
    println(describe(100L))           // Output: Long number
    println(describe(3.14))           // Output: Not a string
    println(describe("Kotlin"))       // Output: Unknown (no matching branch)
}

Output: Each argument matches a different branch. The when expression returns the corresponding string.

when can also be used without an argument for boolean conditions:

fun main() {
    val x = 42
    val y = 100
    
    when {
        x < y -> println("x is less than y")
        x > y -> println("x is greater than y")
        else -> println("x equals y")
    }
}

Output: x is less than y

when is exhaustive. The compiler warns if not all cases are covered. Use else as the default branch.

Ranges

Ranges represent a sequence of values. They are used primarily in loops.

fun main() {
    // Inclusive range: 1, 2, 3, 4, 5
    for (i in 1..5) {
        print("$i ")
    }
    println()
    
    // Exclusive range: 1, 2, 3, 4
    for (i in 1 until 5) {
        print("$i ")
    }
    println()
    
    // Downward range: 5, 4, 3, 2, 1
    for (i in 5 downTo 1) {
        print("$i ")
    }
    println()
    
    // With step: 1, 3, 5
    for (i in 1..10 step 2) {
        print("$i ")
    }
    println()
    
    // Check membership
    val grade = 75
    println(grade in 70..100)  // Output: true
    println(grade !in 90..100) // Output: true
}

Output:

1 2 3 4 5
1 2 3 4
5 4 3 2 1
1 3 5 7 9
true
true

for Loops

The for loop iterates over anything that provides an Iterator.

fun main() {
    // Iterate over a list
    val fruits = listOf("Apple", "Banana", "Cherry")
    for (fruit in fruits) {
        println("I like $fruit")
    }
    
    // Iterate with index
    for ((index, fruit) in fruits.withIndex()) {
        println("#$index: $fruit")
    }
    
    // Iterate over a map
    val scores = mapOf("Alice" to 90, "Bob" to 85)
    for ((name, score) in scores) {
        println("$name scored $score")
    }
    
    // Traditional index loop
    for (i in fruits.indices) {
        println("fruits[$i] = ${fruits[i]}")
    }
}

Output: Each loop iterates over the collection and prints formatted output.

while and do-while Loops

while checks the condition before executing the body. do-while checks after, guaranteeing at least one execution.

fun main() {
    var count = 5
    
    // while loop
    while (count > 0) {
        print("$count ")
        count--
    }
    println()  // Output: 5 4 3 2 1
    
    // do-while loop
    var input: String
    do {
        print("Enter 'quit' to exit: ")
        input = readln()
        println("You entered: $input")
    } while (input != "quit")
    
    println("Goodbye!")  // Output after user types "quit"
}

Output: The while loop counts down from 5 to 1. The do-while loop repeatedly prompts until the user types "quit".

Break and Continue with Labels

Kotlin supports labeled break and continue for nested loops.

fun main() {
    outer@ for (i in 1..3) {
        for (j in 1..3) {
            if (i * j > 4) {
                println("Breaking at i=$i, j=$j")
                break@outer
            }
            println("i=$i, j=$j, product=${i * j}")
        }
    }
}

Output:

i=1, j=1, product=1
i=1, j=2, product=2
i=1, j=3, product=3
i=2, j=1, product=2
i=2, j=2, product=4
Breaking at i=2, j=3

The label outer@ marks the outer loop. break@outer exits the outer loop entirely. Without the label, break would only exit the inner loop.

Common Mistakes

  1. Using when as a statement without exhaustiveness: When used as a statement, the compiler does not enforce exhaustiveness. When used as an expression, all cases must be covered (or else must be present).

  2. Forgetting that if returns a value: In Java, if is a statement. In Kotlin, if is an expression. Treating it as a statement misses opportunities for more concise code.

  3. Off-by-one errors with ranges: The .. operator is inclusive of the end value. Use until for exclusive ranges.

  4. Modifying a collection while iterating: Changing a list while iterating over it with a for loop causes a ConcurrentModificationException. Use an iterator with mutable methods.

  5. Using == instead of === in when branches: when uses structural equality (equals) by default. For referential comparison, use === in the branch condition.

  6. Nesting when without braces: Complex when branches can become hard to read. Use curly braces for multi-line branches.

Practice Questions

  1. How is Kotlin's when expression different from Java's switch?

Answer: when supports arbitrary conditions, type checks, range checks, and does not require break statements. It can be used with or without an argument and returns a value.

  1. What is the difference between 1..5 and 1 until 5?

Answer: 1..5 includes 5 (inclusive range: 1,2,3,4,5). 1 until 5 excludes 5 (exclusive range: 1,2,3,4).

  1. How do you iterate backward through a range?

Answer: Use the downTo function: for (i in 10 downTo 1).

  1. What does the step function do in a range?

Answer: It specifies the increment between consecutive values. 1..10 step 2 produces 1, 3, 5, 7, 9.

  1. Challenge: Write a program that prints a multiplication table for numbers 1 through 10 using nested for loops, formatted as a grid.

Answer:

fun main() {
    for (i in 1..10) {
        for (j in 1..10) {
            print("${i * j}\t".padEnd(4))
        }
        println()
    }
}

Mini Project

Create a number guessing game. Requirements:

  • Generate a random number between 1 and 100
  • Give the user 7 attempts to guess
  • After each guess, tell the user if their guess is too high, too low, or correct
  • Use a while loop for the game loop
  • Use when to determine the response message
  • Track attempts and print a summary at the end

This project practices when expressions, while loops, ranges, and user input handling.

FAQ

Does Kotlin have a ternary operator like Java?

No. Kotlin's if expression serves as both a statement and a ternary operator. Use 'val result = if (condition) a else b' instead of 'condition ? a : b'.

{{< faq "Can when check multiple conditions in one branch?" "Yes. Separate values with commas: 'when (x) { 1, 2, 3 -> println(\"small\") }'. This acts like a logical OR." >}}
Is there a foreach loop in Kotlin?

Use the for loop with 'for (item in collection)' syntax. Kotlin does not have a separate foreach keyword.

How do I skip an iteration in a loop?

Use 'continue'. To skip an outer loop iteration from an inner loop, use labeled continue: 'continue@outerLabel'.

Can I use when with enums?

Yes. when works with enums and the compiler checks exhaustiveness when used as an expression, ensuring all enum constants are handled.

What's Next

Master control flow, then learn functions to organize reusable logic. You can also explore null safety to write safer code with Kotlin's type system.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro