Kotlin/Kotlin inAction 책 읽기

[코틀린] 코틀린 기초 - 선택 표현과 처리: enum과 when

Na선생 2025. 4. 1. 13:07

선택 표현과 처리: enum과 when

1. enum 클래스 정의

Kotlin에서는 enum 클래스를 사용하여 열거형을 정의할 수 있다.

enum class Color {
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
  • enum은 특별한 의미를 가지는 소프트 키워드로, class 키워드 앞에서만 특별한 의미를 가짐.
  • 각 항목은 자동으로 객체 인스턴스화되며, 순서와 이름을 가짐.

1.1 enum 클래스의 프로퍼티와 메서드

enum class Color(val r: Int, val g: Int, val b: Int) {
    RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0),
    GREEN(0, 255, 0), BLUE(0, 0, 255),
    INDIGO(75, 0, 130), VIOLET(238, 130, 238);
    
    fun rgb() = (r * 256 + g) * 256 + b
}

println(Color.BLUE.rgb())
  • 생성자와 프로퍼티를 선언하여 색상 값을 저장.
  • 각 열거형 상수에서 해당하는 RGB 값을 전달해야 함.
  • rgb() 메서드를 정의하여 RGB 값을 계산 가능.
  • 열거형 상수 목록과 메서드 사이에는 반드시 ;(세미콜론)가 필요.

2. when과 enum 클래스

when은 Java의 switch와 유사하지만 더욱 강력한 기능을 제공한다.

fun getMnemonic(color: Color) = when (color) {
    Color.RED -> "Richard"
    Color.ORANGE -> "Of"
    Color.YELLOW -> "York"
    Color.GREEN -> "Gave"
    Color.BLUE -> "Battle"
    Color.INDIGO -> "In"
    Color.VIOLET -> "Vain"
}

println(getMnemonic(Color.BLUE))
  • when은 값을 반환하는 식(expression) 으로 사용 가능.
  • 각 분기에서 break를 명시할 필요 없음.

2.1 여러 값 처리

when을 사용하면 하나의 분기에 여러 값을 설정할 수 있다.

fun getWarmth(color: Color) = when (color) {
    Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
    Color.GREEN -> "neutral"
    Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold"
}

println(getWarmth(Color.ORANGE))
  • ,를 사용하여 여러 값에 대해 동일한 결과를 반환 가능.

3. when과 임의 객체

when을 사용하여 두 개의 색상을 조합하는 예제를 만들어 보자.

fun mix(c1: Color, c2: Color) = when (setOf(c1, c2)) {
    setOf(Color.RED, Color.YELLOW) -> Color.ORANGE
    setOf(Color.YELLOW, Color.BLUE) -> Color.GREEN
    setOf(Color.BLUE, Color.VIOLET) -> Color.INDIGO
    else -> throw Exception("Dirty color")
}

println(mix(Color.BLUE, Color.YELLOW))
  • setOf()를 사용하여 순서와 관계없이 두 색상이 일치하는지 확인.
  • 하지만 매번 새로운 Set 객체를 생성하므로 성능상 비효율적.

3.1 인자 없는 when 사용

불필요한 객체 생성을 줄이려면 when을 인자 없이 사용하면 된다.

fun mixOptimized(c1: Color, c2: Color) = when {
    (c1 == Color.RED && c2 == Color.YELLOW) || (c1 == Color.YELLOW && c2 == Color.RED) -> Color.ORANGE
    (c1 == Color.YELLOW && c2 == Color.BLUE) || (c1 == Color.BLUE && c2 == Color.YELLOW) -> Color.GREEN
    (c1 == Color.BLUE && c2 == Color.VIOLET) || (c1 == Color.VIOLET && c2 == Color.BLUE) -> Color.INDIGO
    else -> throw Exception("Dirty color")
}
  • when에 인자를 전달하지 않고 Boolean 조건식으로 매칭.
  • 객체 생성 없이 조건을 검사할 수 있어 성능이 향상됨.

4. 스마트 캐스트와 타입 검사

Kotlin에서는 is 연산자를 사용하여 객체의 타입을 검사할 수 있다.

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

4.1 if를 사용한 타입 검사

fun eval(e: Expr): Int {
    if (e is Num) {
        return e.value
    }
    if (e is Sum) {
        return eval(e.right) + eval(e.left)
    }
    throw IllegalArgumentException("Unknown expression")
}
  • is 연산자를 사용하여 객체가 특정 클래스의 인스턴스인지 검사.
  • e as Num과 같이 명시적인 캐스팅이 필요 없음 (스마트 캐스트).

4.2 when으로 변경

fun eval(e: Expr): Int = when (e) {
    is Num -> e.value
    is Sum -> eval(e.right) + eval(e.left)
    else -> throw IllegalArgumentException("Unknown expression")
}
  • when을 사용하면 가독성이 향상됨.

4.3 블록 사용

when의 각 분기에서 블록을 사용할 수 있다.

fun evalWithLogging(e: Expr): Int = when (e) {
    is Num -> {
        println("num: ${e.value}")
        e.value
    }
    is Sum -> {
        val left = evalWithLogging(e.left)
        val right = evalWithLogging(e.right)
        println("sum: $left + $right")
        left + right
    }
    else -> throw IllegalArgumentException("Unknown expression")
}
  • 블록의 마지막 표현식이 결과값이 됨.
  • 각 연산 과정을 출력하여 디버깅할 수도 있음.

5. 결론

  • enum 클래스를 사용하면 값의 집합을 정의할 수 있으며, 프로퍼티와 메서드를 추가할 수 있음.
  • when을 사용하면 switch보다 강력한 조건 분기를 구현 가능.
  • 스마트 캐스트를 활용하면 불필요한 명시적 캐스팅을 줄일 수 있음.
  • when을 인자 없이 사용하면 불필요한 객체 생성을 방지할 수 있음.

참고

  • 드미트리 제메로프,스베트라나 이사코바, Kotlin in Action (에이콘출판, 2017)