2025년 12월 31일 작성

Kotlin 제어문 - if, when, for, while

Kotlin의 if와 when은 표현식으로 값을 반환하며, for와 while로 반복을 처리하고, label을 통해 중첩 loop를 제어합니다.

제어문

  • Kotlin의 제어문은 표현식(expression)으로 동작하여 값을 반환할 수 있습니다.
    • ifwhen은 값을 반환하는 표현식입니다.
    • Java의 삼항 연산자(? :)가 없는 대신 if 표현식을 사용합니다.
flowchart LR
    subgraph control[제어문]
        conditional[조건문]
        loop[반복문]
        jump[점프문]
    end

    conditional --> if[if]
    conditional --> when[when]
    loop --> for[for]
    loop --> while[while]
    jump --> break[break]
    jump --> continue[continue]
    jump --> return[return]

if 표현식

  • if문(statement)이 아닌 표현식입니다.
    • 값을 반환하므로 변수에 할당할 수 있습니다.
    • Java의 삼항 연산자를 대체합니다.
// 표현식으로 사용
val max = if (a > b) a else b

// block으로 사용 시 마지막 표현식이 반환값
val max = if (a > b) {
    println("a is bigger")
    a    // 반환값
} else {
    println("b is bigger")
    b    // 반환값
}
  • 표현식으로 사용할 때는 else가 필수입니다.
// compile error : else 필요
// val result = if (condition) "yes"

val result = if (condition) "yes" else "no"    // 정상
  • 문(statement)으로도 사용할 수 있습니다.
if (score >= 90) {
    println("A")
} else if (score >= 80) {
    println("B")
} else {
    println("C")
}

when 표현식

  • whenJava의 switch를 대체하는 강력한 표현식입니다.
    • 다양한 조건을 간결하게 처리합니다.
    • 값을 반환하는 표현식으로 사용할 수 있습니다.
val result = when (x) {
    1 -> "one"
    2 -> "two"
    else -> "other"
}

다양한 조건 Matching

  • when은 여러 가지 형태의 조건을 지원합니다.
when (x) {
    // 여러 값 matching
    1, 2 -> println("1 or 2")

    // 범위 matching
    in 3..10 -> println("between 3 and 10")
    !in 11..20 -> println("not between 11 and 20")

    // type matching
    is String -> println("String of length ${x.length}")

    // 임의의 표현식
    parseInt(input) -> println("matches parsed input")

    else -> println("otherwise")
}

인자 없는 when

  • 인자 없이 if-else chain을 대체할 수 있습니다.
when {
    x < 0 -> println("negative")
    x == 0 -> println("zero")
    x > 0 -> println("positive")
}

val description = when {
    score >= 90 -> "Excellent"
    score >= 80 -> "Good"
    score >= 70 -> "Average"
    else -> "Poor"
}

when과 Smart Cast

  • whenis 조건에서 smart cast가 적용됩니다.
fun describe(obj: Any): String = when (obj) {
    is Int -> "Integer : ${obj * 2}"       // obj가 Int로 cast
    is String -> "String : ${obj.length}"  // obj가 String으로 cast
    is List<*> -> "List : ${obj.size}"     // obj가 List로 cast
    else -> "Unknown"
}

when의 반환값 사용

  • 표현식으로 사용 시 else가 필수입니다.
    • 단, enum이나 sealed class에서 모든 경우를 처리하면 생략 가능합니다.
enum class Color { RED, GREEN, BLUE }

val colorName = when (color) {
    Color.RED -> "Red"
    Color.GREEN -> "Green"
    Color.BLUE -> "Blue"
    // else 불필요 : 모든 enum 값 처리됨
}

for Loop

  • forin keyword로 iterable을 순회합니다.
    • Java의 enhanced for loop와 유사합니다.
    • index 기반 loop는 range를 사용합니다.

Range 순회

  • .. 연산자로 range를 생성합니다.
// 1부터 5까지 (5 포함)
for (i in 1..5) {
    println(i)    // 1, 2, 3, 4, 5
}

// 5부터 1까지 역순
for (i in 5 downTo 1) {
    println(i)    // 5, 4, 3, 2, 1
}

// 2씩 증가
for (i in 1..10 step 2) {
    println(i)    // 1, 3, 5, 7, 9
}

// 마지막 값 제외
for (i in 1 until 5) {
    println(i)    // 1, 2, 3, 4
}

Collection 순회

  • collection의 요소를 직접 순회합니다.
val items = listOf("apple", "banana", "cherry")

for (item in items) {
    println(item)
}
  • index와 함께 순회하려면 withIndex()를 사용합니다.
for ((index, item) in items.withIndex()) {
    println("$index: $item")
}
// 0: apple
// 1: banana
// 2: cherry
  • index만 필요한 경우 indices를 사용합니다.
for (i in items.indices) {
    println("$i: ${items[i]}")
}

Map 순회

  • destructuring으로 key-value를 분리합니다.
val map = mapOf("a" to 1, "b" to 2, "c" to 3)

for ((key, value) in map) {
    println("$key = $value")
}

while과 do-while

  • whiledo-while은 Java와 동일합니다.
// while : 조건을 먼저 검사
var i = 0
while (i < 5) {
    println(i)
    i++
}

// do-while : 최소 한 번 실행 후 조건 검사
var j = 0
do {
    println(j)
    j++
} while (j < 5)

break와 continue

  • break는 loop를 종료합니다.
  • continue는 현재 iteration을 건너뜁니다.
for (i in 1..10) {
    if (i == 3) continue    // 3 건너뜀
    if (i == 7) break       // 7에서 종료
    println(i)
}
// 출력 : 1, 2, 4, 5, 6

Label

  • 중첩 loop에서 외부 loop를 제어하려면 label을 사용합니다.
    • label은 이름@ 형식으로 지정합니다.
outer@ for (i in 1..3) {
    for (j in 1..3) {
        if (i == 2 && j == 2) break@outer    // 외부 loop 종료
        println("$i, $j")
    }
}
// 출력 : 1,1 / 1,2 / 1,3 / 2,1
outer@ for (i in 1..3) {
    for (j in 1..3) {
        if (j == 2) continue@outer    // 외부 loop의 다음 iteration
        println("$i, $j")
    }
}
// 출력 : 1,1 / 2,1 / 3,1

return

  • return은 가장 가까운 함수에서 반환합니다.
fun findFirst(numbers: List<Int>, target: Int): Int? {
    for (n in numbers) {
        if (n == target) return n    // 함수에서 반환
    }
    return null
}

Lambda에서의 return

  • lambda 내부에서 return바깥 함수를 종료(non-local return)합니다.
fun process(numbers: List<Int>) {
    numbers.forEach {
        if (it == 0) return    // process 함수 종료
        println(it)
    }
    println("Done")    // 0이 있으면 실행 안 됨
}
  • lambda만 종료하려면 label을 사용합니다.
fun process(numbers: List<Int>) {
    numbers.forEach {
        if (it == 0) return@forEach    // lambda만 종료 (continue처럼 동작)
        println(it)
    }
    println("Done")    // 항상 실행
}
  • 암시적 label은 함수 이름과 동일합니다.
numbers.forEach {
    if (it == 0) return@forEach    // forEach가 암시적 label
}

// 명시적 label 지정
numbers.forEach myLabel@{
    if (it == 0) return@myLabel
}

Reference


목차