skip to content

Explain comma-grouped branches and `in`/`!in` range checks in `when`. What does comma between conditions mean?

level: middleimportance: should knowfreq 55%

answer

  1. Comma = OR within a branch
  2. `in`/`!in` -> contains()/!contains()
  3. Works with ranges, collections, custom contains
  4. Mix equality and `in` in one branch
  5. No AND via comma — use && in subjectless form

basics

~10 s

A comma between conditions means OR: the branch matches if any listed value matches. in range checks membership and !in checks non-membership, working with ranges, collections, or anything defining contains.

solid answer

~40 s

In a subject `when`, you can list several conditions separated by commas — `1, 2, 3 -> ...` — which is an OR: the branch fires if the subject equals any of them. You can mix kinds in one branch, e.g. `0, in 10..20 -> ...`. The `in`/`!in` operators test containment: `in 1..10` (IntRange), `in listOf("a","b")` (any `Collection`), or any type defining `operator fun contains`. `!in` is the negation. These compile to `range.contains(x)` / `!range.contains(x)`. Comma grouping reduces duplication versus repeating branches and reads clearly. Note commas are OR within one branch — there's no AND syntax across comma-listed conditions; use `&&` inside a single boolean condition (typically in the subjectless form) for AND.

code

kotlin · 7 lines
kotlin
fun statusText(code: Int): String = when (code) {
    200, 201, 204 -> "success"
    in 300..399 -> "redirect"
    in 400..499 -> "client error"
    !in 100..599 -> "invalid"
    else -> "server error"
}

go deeper

for a junior

Uses comma branches and basic in range checks for simple cases.

for a middle

Explains comma = OR, !in negation, and that in desugars to contains for ranges and collections.

for a senior

Notices allocation/performance of in collection literals and chooses Set vs Range deliberately.

for a principal

Defines team conventions for readable membership checks and custom contains operators for domain types.

## Comma-grouped branches (OR) When a subject is present, multiple conditions on one branch are separated by commas and mean **logical OR**: ```kotlin when (key) { "ctrl", "alt", "shift" -> modifier() // matches any of the three else -> normalKey() } ``` The branch matches if the subject equals **any** listed value. This avoids repeating the same body across separate branches. ## `in` / `!in` containment The `in` operator checks **membership**; `!in` is its negation: ```kotlin when (n) { in 1..9 -> "single digit" // IntRange.contains(n) in 10..99 -> "two digits" !in 0..Int.MAX_VALUE -> "negative" // negation else -> "big" } ``` `in` works with anything that defines `operator fun contains`, including: - **Ranges**: `1..10`, `'a'..'z'`, `1..10 step 2` (via `IntProgression`). - **Collections**: `in listOf(...)`, `in setOf(...)`. - **Strings**: `'x' in "text"`, or substring via custom logic. - **Custom types** that define `contains`. `x in r` compiles to `r.contains(x)`, and `x !in r` to `!r.contains(x)`. ## Mixing kinds in one branch You can combine equality and containment with commas: ```kotlin when (code) { 0, in 200..299 -> "ok" // equals 0 OR in 200..299 in 400..499 -> "client error" else -> "other" } ``` ## OR only — no AND across commas Comma grouping is strictly OR. There is **no** comma-based AND. For AND logic, write a single boolean condition with `&&`, which usually means the subjectless form: `when { x > 0 && x < 10 -> ... }`. ## Why it matters Comma grouping and `in` keep branch logic declarative and dense without losing readability, and they let `when` express set/range membership that a constant-only `switch` cannot.

  • Does `in listOf(...)` allocate a new list each call?
    Yes — that literal builds a new list per evaluation; hoist it to a constant/`val` if it's hot, or use a `Set` for O(1) membership.
  • Can you express AND with commas?
    No — commas are OR only. Use a single `&&` condition (usually in the subjectless form).

saying these in an interview costs you the question

  • Thinking comma means AND
  • Believing `in` only works with numeric ranges
  • Not knowing `in` desugars to `contains()`
  • Allocating a fresh `listOf(...)` in a hot `in` check without noticing

context