Conditions and loops
Kotlin gives you flexible tools to control your program's flow. Use if, when, and loops to define clear, expressive logic for your conditions.
If expression
To use if in Kotlin, add the condition to check within parentheses () and the action to take if the result is true within curly braces {}. You can use else and else if for additional branches and checks.
You can also write if as an expression, which lets you assign its returned value directly to a variable. In this form, an else branch is required. The if expression serves the same purpose as the ternary operator (condition ? then : else) found in other languages.
For example:
Each branch in an if expression can be a block, where the value of the last expression becomes the result:
When expressions and statements
when is a conditional expression that runs code based on multiple possible values or conditions. It's similar to the switch statement in Java, C, and other languages. when evaluates its argument and compares the result against each branch in order until one branch condition is satisfied. For example:
You can use when either as an expression or a statement. As an expression, when returns a value you can use later in your code. As a statement, when completes an action without returning a result:
Expression | Statement |
|---|---|
// Returns a string assigned to the
// text variable
val text = when (x) {
1 -> "x == 1"
2 -> "x == 2"
else -> "x is neither 1 nor 2"
}
|
// Returns no result but triggers a
// print statement
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> print("x is neither 1 nor 2")
}
|
Secondly, you can use when with or without a subject. The behavior stays the same either way. Using a subject usually makes your code more readable and maintainable because it clearly shows what you're checking.
With subject | Without subject |
|---|---|
when(x) { ... }
|
when { ... }
|
How you use when determines whether you need to cover all possible cases in your branches. Covering all possible cases is called being exhaustive.
Statements
If you use when as a statement, you don't need to cover all possible cases. In this example, some cases aren't covered, so no branch is triggered. However, no error occurs:
Just like with if, each branch can be a block, and its value is the value of the last expression in the block.
Expressions
If you use when as an expression, you must cover all possible cases. The value of the first matching branch becomes the value of the overall expression. If you don't cover all cases, the compiler throws an error.
If your when expression has a subject, you can use an else branch to make sure that all possible cases are covered, but it isn't mandatory. For example, if your subject is a Boolean, enum class, sealed class, or one of their nullable counterparts, you can cover all cases without an else branch:
If your when expression doesn't have a subject, you must have an else branch or the compiler throws an error. The else branch is evaluated when none of the other branch conditions are satisfied:
Other ways to use when
when expressions and statements offer different ways to simplify your code, handle multiple conditions, and perform type checks.
Group multiple conditions into a single branch using commas:
Use expressions that evaluate to true or false as branch conditions:
Check whether a value is or isn't contained in a range or collection using the in or !in keywords:
Check a value's type using the is or !is keywords. Due to smart casts, you can access the member functions and properties of the type directly:
Use when instead of a traditional if-else if chain. Without a subject, the branch conditions are simply boolean expressions. The first branch with a true condition runs:
Finally, capture the subject in a variable by using the following syntax:
The scope of a variable introduced as the subject is restricted to the body of the when expression or statement.
Guard conditions
Guard conditions allow you to include more than one condition to the branches of a when expression or statement, making complex control flow more explicit and concise. You can use guard conditions with when as long as it has a subject.
Place a guard condition after the primary condition in the same branch, separated by if:
You can't use guard conditions when you have multiple conditions separated by a comma. For example:
In a single when expression or statement, you can combine branches with and without guard conditions. The code in a branch with a guard condition runs only if both the primary condition and the guard condition evaluate to true. If the primary condition doesn't match, the guard condition isn't evaluated.
Since when statements don't need to cover all cases, using guard conditions in when statements without an else branch means that if no conditions match, no code is run.
Unlike statements, when expressions must cover all cases. If you use guard conditions in when expressions without an else branch, the compiler requires you to handle every possible case to avoid runtime errors.
Combine multiple guard conditions within a single branch using the boolean operators && (AND) or || (OR). Use parentheses around the boolean expressions to avoid confusion:
Guard conditions also support else if:
For loops
Use the for loop to iterate through a collection, array, or range:
The body of a for loop can be a block with curly braces {}.
Ranges
To iterate over a range of numbers, use a range expression with .. and ..< operators:
Arrays
If you want to iterate through an array or a list with an index, you can use the indices property:
Alternatively, you can use the .withIndex() function from the standard library:
Iterators
The for loop iterates through anything that provides an iterator. Collections provide iterators by default, whereas ranges and arrays are compiled into index-based loops.
You can create your own iterators by providing a member or extension function called iterator() that returns an Iterator<>. The iterator() function must have a next() function and a hasNext() function that returns a Boolean.
The easiest way to create your own iterator for a class is to inherit from the Iterable<T> interface and override the iterator(), next(), and hasNext() functions that are already there. For example:
Alternatively, you can create the functions from scratch. In this case, add the operator keyword to the functions:
While loops
while and do-while loops run the code in their body continuously while the condition is satisfied. The difference between them is the condition checking time:
whilechecks the condition and, if it's satisfied, runs the code in its body and then returns to the condition check.do-whileruns the code in its body and then checks the condition. If it's satisfied, the loop repeats. So, the body ofdo-whileruns at least once, regardless of the condition.
For a while loop, place the condition to check in parentheses () and the body within curly braces {}:
For a do-while loop, place the body within curly braces {} first before the condition to check in parentheses ():
Break and continue in loops
Kotlin supports traditional break and continue operators in loops. See Returns and jumps.