How to control flow in Kotlin

if

The if statement allows to execute code based on the evaluation of one or more conditions.

In Kotlin if can be an expression that returns last value of <then_block> or <else_block>. Therefore, there is no ternary operator cond?then:else .

else block is required when if is used as expression.

// full syntax
// if(<cond>) {<then_block>} else <else_block>

var max: Int
if (a > b) {
    max = a
} else {
    max = b
}

// as expression
val max = if (a > b) a else b

when(x)

The when keyword defines a conditional expression with multiple branches. It matches its argument against all branches sequentially until some branch condition is satisfied.

In Kotlin when can be used as an expression that returns last value in the corresponding branch.

The else branch is evaluated if none of the other branch conditions are satisfied.

Unlike switch in Java, you can use arbitrary expressions (not only constants) as branch conditions.

when (x) {
    1 -> print("x == 1")
    2, 3 -> print("x is 2 or 3")
    in 20..30 -> print("x is in the range 20..30")
    in validNumbers -> print("x is valid")
    !in 40..50 -> print("x is outside the range 40..50")
    else -> print("none of the above")
}

You can use is or !is operators

fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}

You can capture when subject in a variable using following syntax:

fun Request.getBody() =
        when (val response = executeRequest()) {
            is Success -> response.body
            is HttpError -> throw HttpException(response.status)
        }

when

If no argument is supplied to the when, the branch conditions are simply boolean expressions, and a branch is executed when its condition is true.

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("y is even")
    else -> print("x+y is odd")
}

for each

The for loop iterates through anything that provides an iterator. This means that it:

  • has a member or an extension-function iterator() and the return type of iterator():
    • has a member or an extension-function next()
    • has a member or an extension-function hasNext() that returns Boolean

All of these three functions need to be marked as operator.

for (item in iterable) {
    print(item)
}

For collections you can use the forEach method.

You can iterate through an array or a list with an index:

for (i in array.indices) {
    println(array[i])
}

Alternatively, you can use the withIndex() function:

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

for

You can iterate over range of numbers, in this case iterator not used.

for (i in 1..3) { // i in [1..3]
    println(i)
}

for (i until 1..3) { // i in [1..3)
    println(i)
}

for (i in 6 downTo 0 step 2) { // i: 6,4,2,0
    println(i)
}

while

while checks the condition and, if it's satisfied, executes the body.

// full syntax
// while(<cond>){
//     <body>
//}

while (x > 0) {
    x--
}

do-while

do-while executes the body and then checks the condition. If it's satisfies, the loop repeats.

// full syntax
// do{
//     <body>
//} while(<cond>)

do {
    val y = retrieveData()
} while (y != null) // y is visible here!

labels

Any expression in Kotlin may be marked with a label. Labels have the form of an identifier followed by the @ sign, for example: abc@.

To label an expression, just add a label in front of it.

loop@ for (i in 1..100) {
    // ...
}

break

The break keyword allows to terminate the nearest enclosing loop.

In case of nested loops, you can use labels to terminate the specified loop.

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (...) break@loop
    }
}

continue

The continue keyword allows to proceed to the next step of the nearest enclosing loop.

In case of nested loops, you can use labels to proceed the specified loop.

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (...) continue@loop
    }
}

return

By default return returns from the nearest enclosing function or anonymous function (but not lambdas).

You can specify a label to exit from labeled expression.


fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return // non-local return directly to the caller of foo()
        print(it)
    }
    println("this point is unreachable")
}
fun foo() { listOf(1, 2, 3, 4, 5).forEach { // local return to the caller of the lambda - the forEach loop // @forEach is default label if (it == 3) return@forEach print(it) } print(" done with implicit label") }
fun foo() { listOf(1, 2, 3, 4, 5).forEach lit@{ // local return to the caller of the lambda - the forEach loop if (it == 3) return@lit print(it) } print(" done with explicit label") }