diff --git a/docs/topics/advent-of-code.md b/docs/topics/advent-of-code.md index 2b223e985b6..80dbefae011 100644 --- a/docs/topics/advent-of-code.md +++ b/docs/topics/advent-of-code.md @@ -230,7 +230,7 @@ or watch the video: ### Day 4: Passport processing -Apply the [`when`](control-flow.md#when-expression) expression and explore different ways of how to validate the input: +Apply the [`when`](control-flow.md#when-expressions-and-statements) expression and explore different ways of how to validate the input: utility functions, working with ranges, checking set membership, and matching a particular regular expression. * Read the puzzle description on [Advent of Code](https://adventofcode.com/2020/day/4) diff --git a/docs/topics/basic-syntax.md b/docs/topics/basic-syntax.md index 88ebbe3e92c..f56133c4c76 100644 --- a/docs/topics/basic-syntax.md +++ b/docs/topics/basic-syntax.md @@ -437,7 +437,7 @@ fun main() { ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3" id="kotlin-basic-syntax-when-expression"} -See [when expression](control-flow.md#when-expression). +See [when expressions and statements](control-flow.md#when-expressions-and-statements). ## Ranges diff --git a/docs/topics/control-flow.md b/docs/topics/control-flow.md index 6b3e692d319..33d4d4deeb3 100644 --- a/docs/topics/control-flow.md +++ b/docs/topics/control-flow.md @@ -27,10 +27,12 @@ fun main() { // You can also use `else if` in expressions: val maxLimit = 1 val maxOrLimit = if (maxLimit > a) maxLimit else if (a > b) a else b - - //sampleEnd + println("max is $max") + // max is 3 println("maxOrLimit is $maxOrLimit") + // maxOrLimit is 3 + //sampleEnd } ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3" id="if-else-if-kotlin"} @@ -50,33 +52,125 @@ val max = if (a > b) { If you're using `if` as an expression, for example, for returning its value or assigning it to a variable, the `else` branch is mandatory. -## When expression +## When expressions and statements + +`when` is a conditional expression that runs code based on multiple possible values or conditions. It is +similar to the `switch` statement in Java, C, and similar languages. For example: + +```kotlin +fun main() { + //sampleStart + val x = 2 + when (x) { + 1 -> print("x == 1") + 2 -> print("x == 2") + else -> print("x is neither 1 nor 2") + } + // x == 2 + //sampleEnd +} +``` +{kotlin-runnable="true" kotlin-min-compiler-version="1.3" id="kotlin-conditions-when-statement"} + +`when` matches its argument against all branches sequentially until some branch condition is satisfied. + +You can use `when` in a few different ways. Firstly, you can use `when` either as an **expression** or as a **statement**. +As an expression, `when` returns a value for later use in your code. As a statement, `when` completes an action +without returning anything of further use: + + + + + + + + + + +
ExpressionStatement
+ +```kotlin +// 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" +} +``` -`when` defines a conditional expression with multiple branches. It is similar to the `switch` statement in C-like languages. -Its simple form looks like this. + ```kotlin +// Returns nothing but triggers a +// print statement when (x) { 1 -> print("x == 1") 2 -> print("x == 2") - else -> { - print("x is neither 1 nor 2") - } + else -> print("x is neither 1 nor 2") } ``` -`when` matches its argument against all branches sequentially until some branch condition is satisfied. +
-`when` can be used either as an expression or as a statement. If it is used as an expression, the value -of the first matching branch becomes the value of the overall expression. If it is used as a statement, the values of -individual branches are ignored. Just like with `if`, each branch can be a block, and its value -is the value of the last expression in the block. +Secondly, you can use `when` with or without a subject. Whether you use a subject with `when` or not, your expression or +statement behaves the same. We recommend using `when` with a subject when possible, as it makes your code easier to read +and maintain by clearly showing what you're checking. -The `else` branch is evaluated if none of the other branch conditions are satisfied. + + + + + + + + + +
With subject xWithout subject
-If `when` is used as an _expression_, the `else` branch is mandatory, -unless the compiler can prove that all possible cases are covered with branch conditions, -for example, with [`enum` class](enum-classes.md) entries and [`sealed` class](sealed-classes.md) subtypes). +```kotlin +when(x) { ... } +``` + + + +```kotlin +when { ... } +``` + +
+ +Depending on how you use `when`, there are different requirements for whether you need to cover all possible cases in your +branches. + +If you use `when` as a statement, you don't have to cover all possible cases. In this example, some cases aren't covered, +so nothing happens. However, no error occurs: + +```kotlin +fun main() { + //sampleStart + val x = 2 + when (x) { + // Not all cases are covered + 1 -> print("x == 1") + 2 -> print("x == 2") + } + // x == 2 + //sampleEnd +} +``` +{kotlin-runnable="true" kotlin-min-compiler-version="1.3" id="kotlin-when-statement"} + +In a `when` statement, the values of individual branches are ignored. Just like with `if`, each branch can be a block, +and its value is the value of the last expression in the block. + +If you use `when` as an expression, you have to cover all possible cases. In other words, it must be _exhaustive_. +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](enum-classes.md), [`sealed` class](sealed-classes.md), +or one of their nullable counterparts, you can cover all cases without an `else` branch: ```kotlin enum class Bit { @@ -84,37 +178,27 @@ enum class Bit { } val numericValue = when (getRandomBit()) { + // No else branch is needed because all cases are covered Bit.ZERO -> 0 Bit.ONE -> 1 - // 'else' is not required because all cases are covered } ``` -In `when` _statements_, the `else` branch is mandatory in the following conditions: -* `when` has a subject of a `Boolean`, [`enum`](enum-classes.md), -or [`sealed`](sealed-classes.md) type, or their nullable counterparts. -* branches of `when` don't cover all possible cases for this subject. +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: ```kotlin -enum class Color { - RED, GREEN, BLUE -} - -when (getColor()) { - Color.RED -> println("red") - Color.GREEN -> println("green") - Color.BLUE -> println("blue") - // 'else' is not required because all cases are covered -} - -when (getColor()) { - Color.RED -> println("red") // no branches for GREEN and BLUE - else -> println("not red") // 'else' is required +when { + a > b -> "a is greater than b" + a < b -> "a is less than b" + else -> "a is equal to b" } ``` +`when` expressions and statements offer different ways to simplify your code, handle multiple conditions, and perform +type checks. -To define a common behavior for multiple cases, combine their conditions in a single line with a comma: +You can define a common behavior for multiple cases by combining their conditions in a single line with a comma: ```kotlin when (x) { @@ -123,7 +207,7 @@ when (x) { } ``` -You can use arbitrary expressions (not only constants) as branch conditions +You can use arbitrary expressions (not only constants) as branch conditions: ```kotlin when (x) { @@ -132,7 +216,7 @@ when (x) { } ``` -You can also check a value for being `in` or `!in` a [range](ranges.md) or a collection: +You can also check whether a value is or isn't contained in a [range](ranges.md) or collection via the `in` or `!in` keywords: ```kotlin when (x) { @@ -143,9 +227,9 @@ when (x) { } ``` -Another option is checking that a value `is` or `!is` of a particular type. Note that, -due to [smart casts](typecasts.md#smart-casts), you can access the methods and properties of the type without -any extra checks. +Additionally, you can check that a value is or isn't a particular type via the `is` or `!is` keywords. Note that, +due to [smart casts](typecasts.md#smart-casts), you can access the member functions and properties of the type without +any additional checks. ```kotlin fun hasPrefix(x: Any) = when(x) { @@ -154,8 +238,8 @@ fun hasPrefix(x: Any) = when(x) { } ``` -`when` can also be used as a replacement for an `if`-`else` `if` chain. -If no argument is supplied, the branch conditions are simply boolean expressions, and a branch is executed when its condition is true: +You can use `when` as a replacement for an `if`-`else` `if` chain. +If there's no subject, the branch conditions are simply boolean expressions, and a branch is run when its condition is true: ```kotlin when { @@ -165,7 +249,7 @@ when { } ``` -You can capture *when* subject in a variable using following syntax: +You can capture the subject in a variable by using the following syntax: ```kotlin fun Request.getBody() = @@ -175,7 +259,7 @@ fun Request.getBody() = } ``` -The scope of variable introduced in *when* subject is restricted to the body of this *when*. +The scope of a variable introduced as the subject is restricted to the body of the `when` expression or statement. ## For loops @@ -208,11 +292,12 @@ To iterate over a range of numbers, use a [range expression](ranges.md): fun main() { //sampleStart for (i in 1..3) { - println(i) + print(i) } for (i in 6 downTo 0 step 2) { - println(i) + print(i) } + // 1236420 //sampleEnd } ``` @@ -227,8 +312,9 @@ fun main() { val array = arrayOf("a", "b", "c") //sampleStart for (i in array.indices) { - println(array[i]) + print(array[i]) } + // abc //sampleEnd } ``` @@ -243,6 +329,9 @@ fun main() { for ((index, value) in array.withIndex()) { println("the element at $index is $value") } + // the element at 0 is a + // the element at 1 is b + // the element at 2 is c //sampleEnd } ``` @@ -250,11 +339,11 @@ fun main() { ## While loops -`while` and `do-while` loops execute their body continuously while their condition is satisfied. +`while` and `do-while` loops process their body continuously while their condition is satisfied. The difference between them is the condition checking time: -* `while` checks the condition and, if it's satisfied, executes the body and then returns to the condition check. -* `do-while` executes the body and then checks the condition. If it's satisfied, the loop repeats. So, the body of `do-while` -executes at least once regardless of the condition. +* `while` checks the condition and, if it's satisfied, processes the body and then returns to the condition check. +* `do-while` processes the body and then checks the condition. If it's satisfied, the loop repeats. So, the body of `do-while` +runs at least once regardless of the condition. ```kotlin while (x > 0) { diff --git a/docs/topics/keyword-reference.md b/docs/topics/keyword-reference.md index 0fd0c163797..aaf0f265a56 100644 --- a/docs/topics/keyword-reference.md +++ b/docs/topics/keyword-reference.md @@ -21,19 +21,19 @@ The following tokens are always interpreted as keywords and cannot be used as id - specifies the object being iterated in a [for loop](control-flow.md#for-loops). - is used as an infix operator to check that a value belongs to [a range](ranges.md), a collection, or another entity that [defines a 'contains' method](operator-overloading.md#in-operator). - - is used in [when expressions](control-flow.md#when-expression) for the same purpose. + - is used in [when expressions](control-flow.md#when-expressions-and-statements) for the same purpose. - marks a type parameter as [contravariant](generics.md#declaration-site-variance). * `!in` - is used as an operator to check that a value does NOT belong to [a range](ranges.md), a collection, or another entity that [defines a 'contains' method](operator-overloading.md#in-operator). - - is used in [when expressions](control-flow.md#when-expression) for the same purpose. + - is used in [when expressions](control-flow.md#when-expressions-and-statements) for the same purpose. * `interface` declares an [interface](interfaces.md). * `is` - checks that [a value has a certain type](typecasts.md#is-and-is-operators). - - is used in [when expressions](control-flow.md#when-expression) for the same purpose. + - is used in [when expressions](control-flow.md#when-expressions-and-statements) for the same purpose. * `!is` - checks that [a value does NOT have a certain type](typecasts.md#is-and-is-operators). - - is used in [when expressions](control-flow.md#when-expression) for the same purpose. + - is used in [when expressions](control-flow.md#when-expressions-and-statements) for the same purpose. * `null` is a constant representing an object reference that doesn't point to any object. * `object` declares [a class and its instance at the same time](object-declarations.md). * `package` specifies the [package for the current file](packages.md). @@ -51,7 +51,7 @@ The following tokens are always interpreted as keywords and cannot be used as id * `typeof` is reserved for future use. * `val` declares a read-only [property](properties.md) or [local variable](basic-syntax.md#variables). * `var` declares a mutable [property](properties.md) or [local variable](basic-syntax.md#variables). - * `when` begins a [when expression](control-flow.md#when-expression) (executes one of the given branches). + * `when` begins a [when expression](control-flow.md#when-expressions-and-statements) (executes one of the given branches). * `while` begins a [while loop](control-flow.md#while-loops) (a loop with a precondition). ## Soft keywords @@ -153,7 +153,7 @@ Kotlin supports the following operators and special symbols: * `->` - separates the parameters and body of a [lambda expression](lambdas.md#lambda-expression-syntax). - separates the parameters and return type declaration in a [function type](lambdas.md#function-types). - - separates the condition and body of a [when expression](control-flow.md#when-expression) branch. + - separates the condition and body of a [when expression](control-flow.md#when-expressions-and-statements) branch. * `@` - introduces an [annotation](annotations.md#usage). - introduces or references a [loop label](returns.md#break-and-continue-labels). diff --git a/docs/topics/sealed-classes.md b/docs/topics/sealed-classes.md index 85dff5c3626..9ad424df3b2 100644 --- a/docs/topics/sealed-classes.md +++ b/docs/topics/sealed-classes.md @@ -164,7 +164,7 @@ you can create subclasses in any source set between the `expect` and `actual` de ## Use sealed classes with when expression -The key benefit of using sealed classes comes into play when you use them in a [`when`](control-flow.md#when-expression) +The key benefit of using sealed classes comes into play when you use them in a [`when`](control-flow.md#when-expressions-and-statements) expression. The `when` expression, used with a sealed class, allows the Kotlin compiler to check exhaustively that all possible cases are covered. In such cases, you don't need to add an `else` clause: diff --git a/docs/topics/typecasts.md b/docs/topics/typecasts.md index 3c5282e3410..fe8491c81b5 100644 --- a/docs/topics/typecasts.md +++ b/docs/topics/typecasts.md @@ -47,7 +47,7 @@ print(x.length) // x is automatically cast to String ### Control flow -Smart casts work not only for `if` conditional expressions but also for [`when` expressions](control-flow.md#when-expression) +Smart casts work not only for `if` conditional expressions but also for [`when` expressions](control-flow.md#when-expressions-and-statements) and [`while` loops](control-flow.md#while-loops): ```kotlin diff --git a/docs/topics/whatsnew13.md b/docs/topics/whatsnew13.md index 32a8fc6c136..47560d71ecb 100644 --- a/docs/topics/whatsnew13.md +++ b/docs/topics/whatsnew13.md @@ -146,7 +146,7 @@ fun Request.getBody() = ``` While it was already possible to extract this variable just before `when` , `val` in `when` has its scope properly restricted -to the body of `when`, and so preventing namespace pollution. [See the full documentation on `when` here](control-flow.md#when-expression). +to the body of `when`, and so preventing namespace pollution. [See the full documentation on `when` here](control-flow.md#when-expressions-and-statements). ## @JvmStatic and @JvmField in companions of interfaces diff --git a/docs/topics/whatsnew1530.md b/docs/topics/whatsnew1530.md index 6ced83aed5e..a6b41372b01 100644 --- a/docs/topics/whatsnew1530.md +++ b/docs/topics/whatsnew1530.md @@ -32,7 +32,7 @@ and type inference: > {style="warning"} -An _exhaustive_ [`when`](control-flow.md#when-expression) statement contains branches for all possible types or values of its subject or for some types plus an `else` branch. In other words, it covers all possible cases. +An _exhaustive_ [`when`](control-flow.md#when-expressions-and-statements) statement contains branches for either all possible types or values of its subject, or for certain types and includes an `else` branch to cover any remaining cases. We're planning to prohibit non-exhaustive `when` statements soon to make the behavior consistent with `when` expressions. To ensure smooth migration, you can configure the compiler to report warnings about non-exhaustive `when` statements with a sealed class or a Boolean. Such warnings will appear by default in Kotlin 1.6 and will become errors later. diff --git a/docs/topics/whatsnew16.md b/docs/topics/whatsnew16.md index d78090f885f..943318bff51 100644 --- a/docs/topics/whatsnew16.md +++ b/docs/topics/whatsnew16.md @@ -21,7 +21,7 @@ It also includes various type inference improvements and support for annotations ### Stable exhaustive when statements for enum, sealed, and Boolean subjects -An _exhaustive_ [`when`](control-flow.md#when-expression) statement contains branches for all possible types or values of +An _exhaustive_ [`when`](control-flow.md#when-expressions-and-statements) statement contains branches for all possible types or values of its subject, or for some types plus an `else` branch. It covers all possible cases, making your code safer. We will soon prohibit non-exhaustive `when` statements to make the behavior consistent with `when` expressions. diff --git a/docs/topics/whatsnew20.md b/docs/topics/whatsnew20.md index f67095873c5..1e9d742b8e7 100644 --- a/docs/topics/whatsnew20.md +++ b/docs/topics/whatsnew20.md @@ -1526,7 +1526,7 @@ Since Kotlin 2.0.0, the following DSLs for specifying compiler options are depre * The `kotlinOptions` DSL from the `KotlinCompile` interface that implements all Kotlin compilation tasks. Use `KotlinCompilationTask` instead. -* The `compilerOptions` property with the `HasCompilerOptions` type from the `KotlinCompiation` interface. This DSL was +* The `compilerOptions` property with the `HasCompilerOptions` type from the `KotlinCompilation` interface. This DSL was inconsistent with other DSLs and configured the same `KotlinCommonCompilerOptions` object as `compilerOptions` inside the `KotlinCompilation.compileTaskProvider` compilation task, which was confusing.