-
-
Notifications
You must be signed in to change notification settings - Fork 204
Add when concept #742
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add when concept #742
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "authors": [ | ||
| "kahgoh" | ||
| ], | ||
| "contributors": [], | ||
| "blurb": "When is an alternative to long if / else if / else chains." | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| # Introduction | ||
|
|
||
| Kotlin's `when` is a conditional expression for choosing among several options. | ||
| It is similar to `switch` in Java, and more flexible than a chain of `if` ... `else if` ... `else`. | ||
|
|
||
| Like `if`, `when` can also be used as an expression that produces a value. | ||
|
|
||
| ## Matching a value | ||
|
|
||
| Give `when` a value to match in parentheses. | ||
| Each branch compares that value until one matches: | ||
|
|
||
| ```kotlin | ||
| val animal = "chicken" | ||
| val type = when (animal) { | ||
| "chicken" -> "bird" | ||
| "poodle" -> "dog" | ||
| else -> "unknown" | ||
| } | ||
| // type => "bird" | ||
| ``` | ||
|
|
||
| - Each branch is written as `pattern -> result`. | ||
| - Branches are tried from top to bottom until a match is found. | ||
| Only the first branch runs. | ||
| - Use `else` for any value not listed above (similar to the final `else` in an `if` chain). | ||
|
|
||
| This is similar to [pattern matching][pattern-matching]. | ||
| The `pattern` can be a value to match, or use `is` to match by type: | ||
|
|
||
| ```kotlin | ||
| fun typeText(subject: Any): String { | ||
| return when(subject) { | ||
| is Animal -> "Animal" | ||
| is Fruit -> "Fruit" | ||
| is String -> "Text" | ||
| else -> "Unknown" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| or use `in` to check if it is contained in a range, collection, string or anything else with a `contains` method: | ||
|
|
||
| ```kotlin | ||
| fun describe(number: Int, position: String) { | ||
| // Range | ||
| when(number) { | ||
| in 1..10 -> println("From 1 to 10") | ||
| in 11..20 -> println("From 11 to 20") | ||
| } | ||
|
|
||
| // Collection | ||
| when(number) { | ||
| in listOf(2, 4, 6) -> println("small even") | ||
| in listOf(3, 5, 7) -> println("small odd") | ||
| } | ||
|
|
||
| // String | ||
| when(position) { | ||
| in "center middle" -> println("inner") | ||
| in "left right boundary" -> println("outer") | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ~~~exercism/note | ||
| You can also use `in` with other objects by defining the `contains` [operator extension function][kotlin-operator-overloading]. | ||
| For example, using it with [Regex][kotlin-regex]: | ||
|
|
||
| ```kotlin | ||
| operator fun Regex.contains(text: CharSequence): Boolean = this.matches(text) | ||
|
|
||
| fun replace(input: String) { | ||
| return when(input) { | ||
| in Regex("""ani*""") -> println("animal") | ||
| in Regex("""fru.*""") -> println("fruit") | ||
| in Regex("""num.*""") -> println("number") | ||
| else -> println("unknown") | ||
| } | ||
| } | ||
| ``` | ||
| ~~~ | ||
|
|
||
| ## Guard conditions | ||
|
|
||
| When matching a value, additional conditions can be placed in an `if` after the pattern. | ||
| These conditions are called guard conditions. | ||
|
|
||
| ```kotlin | ||
| val x = 5 | ||
| val y = 10 | ||
| val description = when (x) { | ||
| 5 if y % 2 == 0 -> "x is 5, y is even" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does regexp work?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, regexp can work as part of the guard. Something like this: val code = 3
val classification = "animal"
val type = when (code) {
3 if """.*mal""".toRegex().matches(classification) -> "animal"
else -> "unknown"
}But if using it match the value, I think you can only do it by matching conditions (i.e. without a value to match). For example: val classification = "animal"
val type = when {
Regex("''.*mal""").matches(classification) -> "animal"
Regex(''"fru.*""").matches(classification) -> "fruit"
else -> "unknown"
}
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I just learn I can indeed use Regexp but would need to define an extension function: operator fun Regex.contains(text: CharSequence): Boolean = this.matches(text)
fun main() {
val classification = "animal"
val res = when(classification) {
in Regex(""".*mal""") -> println("animal")
in Regex("""fru.*""") -> println("fruit")
else -> println("unknown")
}
println(res)
}
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added something about this in the |
||
| else -> "other" | ||
| } | ||
| // description => "x is 5, y is even" | ||
| ``` | ||
|
|
||
| Even `else if` can be used. | ||
|
|
||
| ```kotlin | ||
| val x = 7 | ||
| val y = 10 | ||
| val description = when (x) { | ||
| 5 if y % 2 == 0 -> "x is 5, y is even" | ||
| else if y % 2 == 0 -> "y is an even number" | ||
| else -> "other" | ||
| } | ||
| // description => "y is an even number" | ||
| ``` | ||
|
|
||
|
|
||
| ## Matching conditions | ||
|
|
||
| Alternatively, the value can be omitted and boolean conditions can be placed on each branch: | ||
|
|
||
| ```kotlin | ||
| val x = 5 | ||
| val y = 8 | ||
| val description = when { | ||
| x % 2 != 0 -> "x is odd" | ||
| y % 2 == 0 -> "y is even" | ||
| else -> "neither condition matched first" | ||
| } | ||
| // description => "x is odd" | ||
| ``` | ||
|
|
||
| Again, branches are checked from top to bottom and only the first branch whose condition is `true` runs. | ||
|
|
||
| ## Using `when` as an expression | ||
|
|
||
| When `when` is used for its result (assigned to a variable, returned from a function, and so on), every possible case must be covered. | ||
|
|
||
| - When matching with a value, add `else` unless every possible value is already handled (for example, all values of a `Boolean` or an `enum`). | ||
| - When matching conditions, an `else` branch is required, because the compiler cannot know that your conditions cover every case. | ||
|
|
||
| ```kotlin | ||
| fun label(n: Int): String = when (n) { | ||
| 0 -> "zero" | ||
| 1 -> "one" | ||
| else -> "other" | ||
| } | ||
| ``` | ||
|
|
||
| [kotlin-operator-overloading]: https://kotlinlang.org/docs/operator-overloading.html | ||
| [kotlin-regex]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.text/-regex/ | ||
| [pattern-matching]: https://en.wikipedia.org/wiki/Pattern_matching | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| # Introduction | ||
|
|
||
| Kotlin's `when` is a conditional expression for choosing among several options. | ||
| It is similar to `switch` in Java, and more flexible than a chain of `if` ... `else if` ... `else`. | ||
|
|
||
| Like `if`, `when` can also be used as an expression that produces a value. | ||
|
|
||
| ## Matching a value | ||
|
|
||
| Give `when` a value to match in parentheses. | ||
| Each branch compares that value until one matches: | ||
|
|
||
| ```kotlin | ||
| val animal = "chicken" | ||
| val type = when (animal) { | ||
| "chicken" -> "bird" | ||
| "poodle" -> "dog" | ||
| else -> "unknown" | ||
| } | ||
| // type => "bird" | ||
| ``` | ||
|
|
||
| - Each branch is written as `pattern -> result`. | ||
| - Branches are tried from top to bottom until a match is found. | ||
| Only the first branch runs. | ||
| - Use `else` for any value not listed above (similar to the final `else` in an `if` chain). | ||
|
|
||
| This is similar to [pattern matching][pattern-matching]. | ||
| The `pattern` can be a value to match, or use `is` to match by type: | ||
|
|
||
| ```kotlin | ||
| fun typeText(subject: Any): String { | ||
| return when(subject) { | ||
| is Animal -> "Animal" | ||
| is Fruit -> "Fruit" | ||
| is String -> "Text" | ||
| else -> "Unknown" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| or use `in` to check if it is contained in a range, collection, string or anything else with a `contains` method: | ||
|
|
||
| ```kotlin | ||
| fun describe(number: Int, position: String) { | ||
| // Range | ||
| when(number) { | ||
| in 1..10 -> println("From 1 to 10") | ||
| in 11..20 -> println("From 11 to 20") | ||
| } | ||
|
|
||
| // Collection | ||
| when(number) { | ||
| in listOf(2, 4, 6) -> println("small even") | ||
| in listOf(3, 5, 7) -> println("small odd") | ||
| } | ||
|
|
||
| // String | ||
| when(position) { | ||
| in "center middle" -> println("inner") | ||
| in "left right boundary" -> println("outer") | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Guard conditions | ||
|
|
||
| When matching a value, additional conditions can be placed in an `if` after the pattern. | ||
| These conditions are called guard conditions. | ||
|
|
||
| ```kotlin | ||
| val x = 5 | ||
| val y = 10 | ||
| val description = when (x) { | ||
| 5 if y % 2 == 0 -> "x is 5, y is even" | ||
| else -> "other" | ||
| } | ||
| // description => "x is 5, y is even" | ||
| ``` | ||
|
|
||
| Even `else if` can be used. | ||
|
|
||
| ```kotlin | ||
| val x = 7 | ||
| val y = 10 | ||
| val description = when (x) { | ||
| 5 if y % 2 == 0 -> "x is 5, y is even" | ||
| else if y % 2 == 0 -> "y is an even number" | ||
| else -> "other" | ||
| } | ||
| // description => "y is an even number" | ||
| ``` | ||
|
|
||
|
|
||
| ## Matching conditions | ||
|
|
||
| Alternatively, the value can be omitted and boolean conditions can be placed on each branch: | ||
|
|
||
| ```kotlin | ||
| val x = 5 | ||
| val y = 8 | ||
| val description = when { | ||
| x % 2 != 0 -> "x is odd" | ||
| y % 2 == 0 -> "y is even" | ||
| else -> "neither condition matched first" | ||
| } | ||
| // description => "x is odd" | ||
| ``` | ||
|
|
||
| Again, branches are checked from top to bottom and only the first branch whose condition is `true` runs. | ||
|
|
||
| ## Using `when` as an expression | ||
|
|
||
| When `when` is used for its result (assigned to a variable, returned from a function, and so on), every possible case must be covered. | ||
|
|
||
| - When matching with a value, add `else` unless every possible value is already handled (for example, all values of a `Boolean` or an `enum`). | ||
| - When matching conditions, an `else` branch is required, because the compiler cannot know that your conditions cover every case. | ||
|
|
||
| ```kotlin | ||
| fun label(n: Int): String = when (n) { | ||
| 0 -> "zero" | ||
| 1 -> "one" | ||
| else -> "other" | ||
| } | ||
| ``` | ||
|
|
||
| [pattern-matching]: https://en.wikipedia.org/wiki/Pattern_matching |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| [ | ||
| { | ||
| "url": "https://kotlinlang.org/docs/control-flow.html#when-expressions-and-statements", | ||
| "description": "Kotlin when expressions and statements" | ||
| } | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is pattern matching then?
Could be something to call out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added mention about pattern matching.