Control Statements

Branching with if and when, iteration with loops, flow control with break/continue, and type-safe smart casts

if / when loops smart cast

Table of Contents

1if / else / else if

Kotlin's if is not only a statement — it can be an expression that returns a value. Use else if chains for multiple conditions, and omit braces for single-line branches when readability allows.

Classic if / else

if-else.ktfun main() { val score = 85 if (score >= 90) { println("Grade: A") } else if (score >= 75) { println("Grade: B") } else { println("Grade: C or below") } }

if as an expression

When if returns a value, both branches must return compatible types. The result can be assigned to a variable:

if expressionfun main() { val age = 20 val status = if (age >= 18) "Adult" else "Minor" println(status) // Adult val max = if (10 > 5) 10 else 5 println("Max: $max") // Max: 10 }

Single-line if (no braces)

Concise iffun main() { val n = 7 if (n % 2 == 0) println("Even") else println("Odd") }
FormExampleUse when
ifif (x > 0) { ... }Single condition
else ifelse if (x > 10) { ... }Multiple exclusive branches
if expressionval y = if (a) 1 else 2Assign result from branches
Prefer when for many branches

Long else if chains are often clearer as a when expression — especially when matching constants, types, or ranges.

2when Expression

when replaces Java's switch. It is exhaustive-friendly, supports ranges and types, and can run with or without a subject argument.

when with a subject (like switch)

when with argumentfun main() { val day = 3 val name = when (day) { 1 -> "Monday" 2 -> "Tuesday" 3 -> "Wednesday" in 4..5 -> "Thursday or Friday" else -> "Weekend or invalid" } println(name) // Wednesday }

when without argument (if-else chain style)

when — no subjectfun describeTemp(celsius: Int): String = when { celsius < 0 -> "Freezing" celsius in 0..15 -> "Cool" celsius in 16..30 -> "Warm" else -> "Hot" } fun main() { println(describeTemp(22)) // Warm }

Multiple conditions in one branch

Comma-separated branchesfun main() { val c = 'e' when (c) { 'a', 'e', 'i', 'o', 'u' -> println("Vowel") else -> println("Consonant") } }

when as expression with blocks

when expressionfun main() { val x = 10 val result = when (x) { 1, 2, 3 -> "Small" in 4..10 -> { println("Computing medium...") "Medium" } else -> "Large" } println(result) // Medium }

3for Loops

Kotlin has no C-style for (i = 0; i < n; i++). Use for (item in collection) or iterate over ranges and progressions.

Ranges — .., until, downTo, step

Range iterationfun main() { // Inclusive: 1, 2, 3, 4, 5 for (i in 1..5) print("$i ") // 1 2 3 4 5 println() // Half-open: 1, 2, 3, 4 (excludes 5) for (i in 1 until 5) print("$i ") // 1 2 3 4 println() // Descending: 5, 4, 3, 2, 1 for (i in 5 downTo 1) print("$i ") println() // Step by 2: 0, 2, 4, 6, 8, 10 for (i in 0..10 step 2) print("$i ") println() }

Collections and arrays

for over collectionfun main() { val fruits = listOf("Apple", "Banana", "Cherry") for (fruit in fruits) { println(fruit) } val scores = intArrayOf(90, 85, 78) for ((index, score) in scores.withIndex()) { println("Index $index: $score") } }
OperatorRangeNotes
..1..5Inclusive both ends
until1 until 5Includes start, excludes end
downTo5 downTo 1Descending inclusive
step0..10 step 2Custom increment
Indices

Use for (i in list.indices) or list.withIndex() when you need the index. Prefer direct iteration when you only need values.

4while and do-while

while checks the condition before each iteration. do-while runs the body at least once, then checks the condition.

while loop

whilefun main() { var count = 3 while (count > 0) { println("Countdown: $count") count-- } }

do-while loop

do-whilefun main() { var input: Int do { print("Enter a positive number: ") input = readln().toIntOrNull() ?: -1 } while (input <= 0) println("You entered: $input") }
LoopCondition checkedBody runs at least once?
whileBefore iterationNo — may never run
do { } whileAfter iterationYes — always runs once

5break, continue, and Labels

break exits the nearest enclosing loop. continue skips to the next iteration. Use labels to target an outer loop in nested structures.

break and continue

break / continuefun main() { for (i in 1..10) { if (i == 3) continue // skip 3 if (i == 8) break // stop at 8 print("$i ") // 1 2 4 5 6 7 } println() }

Labeled break and continue

Labelsfun main() { outer@ for (i in 1..3) { for (j in 1..3) { if (i == 2 && j == 2) break@outer print("($i,$j) ") } } // prints (1,1) (1,2) (1,3) (2,1) then breaks out of outer loop println() }

return@label in inline lambdas

return@forEachfun main() { listOf(1, 2, 3, 4).forEach { if (it == 3) return@forEach // skip only this iteration println(it) // 1, 2, 4 } }

6Smart Casts and Type Checks

Use is to check types, as for unsafe casts, and as? for safe casts. After a successful is check, Kotlin smart casts the variable in that scope.

is — type check

is check + smart castfun printLength(obj: Any) { if (obj is String) { // obj is smart-cast to String here println(obj.length) } else { println("Not a String") } } fun main() { printLength("Kotlin") // 6 printLength(42) // Not a String }

as and as? — explicit casts

Unsafe vs safe castfun main() { val anything: Any = "Hello" val s = anything as String // OK // val bad = anything as Int // ClassCastException at runtime val maybe = anything as? Int // null if cast fails println(maybe) // null }

Smart cast limitations

When smart cast failsfun demo(x: Any) { if (x is String) { // println(x.length) // OK — x is stable parameter } } class Holder { var value: Any = "text" } fun check(holder: Holder) { if (holder.value is String) { // holder.value.length // Error: value is var — could change val local = holder.value if (local is String) { println(local.length) // OK — local val is stable } } }
OperatorPurposeOn failure
isType check (Boolean)N/A — returns false
asUnsafe castClassCastException
as?Safe castReturns null
Smart castAuto-narrow type after isRequires stable, non-mutable reference

7Summary Cheatsheet

TopicKey Takeaway
ifStatement or expression: val x = if (a) 1 else 2
whenReplaces switch; with/without subject; ranges, types, multiple values
forfor (x in collection); ranges: .., until, downTo, step
while / do-whileCondition-first vs body runs at least once
break / continueExit loop or skip iteration; use @label for outer loops
Type checksis + smart cast; as unsafe; as? safe
Next lessonFunctions — parameters, return types, recursion

Kotlin Control Statements MCQ Practice

10 Basic MCQs 10 Advanced MCQs

10 Basic Control Flow MCQs

1

Which Kotlin construct replaces Java's switch statement?

Awhen
Bmatch
Cselect
Dcase
Explanation: when is Kotlin's powerful alternative to switch, supporting expressions, ranges, types, and multiple conditions per branch.
2

In Kotlin, if can be used as?

AA statement only — never returns a value
BAn expression that returns a value from its branches
CA loop construct
DA type cast operator
Explanation: val result = if (condition) a else b treats if as an expression — both branches must yield compatible types.
3

What values does the range 1..5 include when used in a for loop?

A1, 2, 3, 4, 5 (inclusive)
B1, 2, 3, 4 only
C2, 3, 4, 5 only
D5, 4, 3, 2, 1
Explanation: The .. operator creates a closed range — both the start and end values are included in iteration.
4

What does 1 until 5 produce?

A1, 2, 3, 4 (end excluded)
B1, 2, 3, 4, 5
C5, 4, 3, 2, 1
DEmpty range
Explanation: until creates a half-open range — it includes the start but excludes the end value (like many APIs use for indices).
5

A when block without a subject argument is most similar to?

AAn if-else chain with boolean conditions
BA for loop over a list
CA try-catch block
DA class constructor
Explanation: when { x > 0 -> ...; else -> ... } evaluates each branch condition like a series of if / else if checks.
6

In a do-while loop, when is the condition evaluated?

ABefore the first iteration only
BAfter each iteration of the body
CNever — it runs forever
DOnly at compile time
Explanation: do { ... } while (condition) executes the body first, then checks the condition — so the body always runs at least once.
7

What does break@outer do in nested loops?

ABreaks out of the loop labeled outer
BBreaks only the innermost loop always
CThrows an exception
DPauses the program
Explanation: Labels like outer@ let you target a specific enclosing loop with break@outer or continue@outer.
8

After if (x is String) in the same block, Kotlin automatically treats x as String. This is called?

ASmart cast
BUnsafe cast
CReflection
DGenerics erasure
Explanation: Smart casts apply when the compiler can prove the type is stable in that scope after an is check.
9

Which operator performs a safe cast that returns null on failure?

Aas?
Bas
Cis
D!!
Explanation: as? returns null instead of throwing ClassCastException when the cast is not possible.
10

Which range expression counts from 5 down to 1 inclusive?

A5 downTo 1
B5..1
C1 until 5
D5 until 1
Explanation: downTo creates a descending progression; .. always counts upward and cannot produce 5,4,3,2,1 directly.

10 Advanced Control Flow MCQs

11

In when (x) { 1, 2 -> "low"; in 3..10 -> "mid"; else -> "high" }, what does the comma in 1, 2 mean?

AMultiple values share the same branch (OR)
BBoth values must match (AND)
CSyntax error
DCreates a Pair
Explanation: Comma-separated conditions in one when branch act like logical OR — the branch runs if x is 1 or 2.
12

What does for (i in 0..10 step 2) print for i?

A0, 2, 4, 6, 8, 10
B0, 1, 2, 3, ... 10
C10, 8, 6, 4, 2, 0
DOnly 0 and 10
Explanation: step sets the increment between values in a progression while keeping the range endpoints valid.
13

What does continue do inside a loop?

ASkips the rest of the current iteration and proceeds to the next
BExits the entire program
CRestarts the program from main
DThrows NullPointerException
Explanation: continue jumps to the next loop iteration without executing remaining statements in the current pass.
14

The is operator in Kotlin is used to?

ACheck whether an object is an instance of a type (returns Boolean)
BAssign a value to a variable
CImport a package
DDeclare an interface
Explanation: x is String returns true if x is a String at runtime and enables smart casting in that branch.
15

You need a loop that always runs its body at least once before checking the condition. Which do you use?

Awhile only
Bdo-while
Cfor with empty range
Dwhen without else
Explanation: do-while guarantees one execution before the condition is tested — useful for input validation menus.
16

What is the correct syntax to break out of a loop labeled search?

Abreak@search
Bbreak.search
Cgoto search
Dexit(search)
Explanation: Kotlin uses label@ on the loop and break@label / continue@label to control nested iteration.
17

What happens when an unsafe cast obj as Int fails at runtime?

AReturns null
BThrows ClassCastException
CCompiler error only
DSilently returns 0
Explanation: as performs an unchecked cast at runtime — failure throws ClassCastException. Use as? when failure should yield null.
18

Smart casting often fails for a property declared as?

Avar (mutable) that another thread could change
Bconst val only
CLocal val after is check
DFunction parameters
Explanation: The compiler cannot smart-cast mutable var properties because their type might change between the check and usage — copy to a local val instead.
19

In for (i in 1..10 step 3), what is the role of step?

AChanges the increment between successive values
BDeclares a new variable
CReverses the loop direction only
DReplaces the need for break
Explanation: step n advances by n each iteration (e.g. 1, 4, 7, 10 for step 3 in 1..10).
20

When when is used as an expression that returns a value, it must be?

AExhaustive — cover all cases or include else
BEmpty with no branches
CWritten inside a class only
DWithout any branches
Explanation: Expression when must assign a value on every path — use else or cover all enum/sealed cases so the compiler can verify exhaustiveness.
Click an option to select, then check answers.

Kotlin Control Statements Interview Q&A

15 topic-focused questions for interviews and revision.

1What is the difference between if as a statement and if as an expression?easy
Answer: As a statement, if runs a branch for side effects only. As an expression, it returns a value: val max = if (a > b) a else b. Expression form requires both branches to return compatible types.
2How does Kotlin's when compare to Java's switch?easy
Answer: when supports arbitrary expressions, ranges (in 1..10), types (is String), multiple values per branch, and can be used with or without a subject. It is more flexible and idiomatic than classic switch.
3Explain .. versus until in Kotlin ranges.easy
Answer: 1..5 is inclusive on both ends (1 through 5). 1 until 5 includes 1 but excludes 5, yielding 1–4. Use until when the end index should not be processed — common for zero-based length loops.
4When would you use downTo and step?medium
Answer: downTo iterates descending (e.g. 10 downTo 1). step customizes increment size (e.g. 0..100 step 10 for every tenth value). Combine them: 100 downTo 0 step 10.
5What is the difference between while and do-while?easy
Answer: while evaluates the condition before the body — the body may never execute. do-while runs the body first, then checks the condition, guaranteeing at least one execution.
6How do labeled break and continue work?medium
Answer: Prefix a loop with a label such as outer@ for (...). Use break@outer or continue@outer to affect that specific loop instead of only the innermost one — essential for nested search loops.
7What is a smart cast in Kotlin?medium
Answer: After a successful is type check, the compiler automatically narrows the variable's type in that scope (e.g. call .length on a value checked as String) without an explicit as cast, when the reference is stable.
8When does smart casting fail?hard
Answer: Smart casts fail for mutable var properties (another thread could change the type), custom getters the compiler cannot analyze, and when the variable is captured in a lambda that might run later. Copy to a local val after checking.
9Compare as and as?.easy
Answer: as is an unsafe cast — failure throws ClassCastException. as? is a safe cast returning null on failure, ideal for optional narrowing of platform types or mixed Any values.
10Can when replace a long else if chain? Give an example.medium
Answer: Yes. Instead of many else if branches, use when (score) { in 90..100 -> "A"; in 75..89 -> "B"; else -> "C" } or a subject-less when { score >= 90 -> ... } for boolean conditions.
11Does Kotlin have a traditional C-style for (i = 0; i < n; i++) loop?easy
Answer: No. Use for (i in 0 until n) or for (i in 0..<n) (Kotlin 1.7+ range syntax where supported) or iterate collections directly with for (item in list).
12What does return@forEach mean inside a lambda?hard
Answer: It is a non-local return from the lambda to the labeled function — here, skipping only the current element's iteration (like continue), not exiting main. The label matches the inline function name (forEach, run, etc.).
13How do you match multiple constants in one when branch?easy
Answer: Separate values with commas: when (c) { 'a', 'e', 'i' -> "Vowel"; else -> "Consonant" }. You can also combine with ranges: in 1..3, 7 -> ... in advanced cases.
14Write an if expression that assigns a grade letter from a numeric score.medium
Answer: val grade = when { score >= 90 -> "A"; score >= 75 -> "B"; else -> "C" } or nested if: val grade = if (score >= 90) "A" else if (score >= 75) "B" else "C".
15Find the first even number greater than 10 in a list using control flow.hard
Answer: for (n in numbers) { if (n > 10 && n % 2 == 0) { println(n); break } } or numbers.first { it > 10 && it % 2 == 0 }. break exits once found; first is the idiomatic functional alternative.