본문으로 바로가기

[Kotlin] 코틀린 기초 #2 enum, when

category 개발이야기/Kotlin 2018. 4. 11. 23:34
반응형

이 글은 Kotlin In Action을 참고 하였습니다.

더욱 자세한 설명이나 예제는 직접 책을 구매하여 확인 하시기 바랍니다

2.3 enum 과 when

2.3.1 enum

enum은 아래와 같이 사용합니다.
enum class Color {
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}

물론 이건 기본적인 형태이지만, 흔히 java에서 사용할때 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
}

fun main(args: Array) {
    println(Color.BLUE.rgb())
}

enum 내부에 값과 함수까지 정의했습니다.

코틀린에서는 ; 를 사용하지 않지만 enum의 마지막에는 꼭 사용해야합니다. 


2.3.2 when에 enum을 사용

when은 자바와 다르게 좀더 확장성 있게 object를 지원합니다.

enum은 java에서도 지원하고, 당연히 kotlin에서 지원합니다.

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"
    }

fun main(args: Array) {
    println(getMnemonic(Color.BLUE))
}

when의 특징은 break;를 넣지 않아도 된다는 점입니다.

따라서 break를 빼먹어서 생기는 오류를 막을 수 있습니다.


만약에 여러값을 하나의 분기로 처리하고자 한다면 콤마 "," 를 사용하면 됩니다.
fun getWarmth(color: Color) = when(color) {
    Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
    Color.GREEN -> "neutral"
    Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold"
}


2.3.3 when의 인자로 object 사용

when은 object를 인자로 받을수 있기 때문에 아래와 같은 활용도 가능합니다.

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

fun main(args: Array) {
    println(mix(BLUE, YELLOW))
}

setOf는 java로 얘기하면 set을 만드는 함수 입니다.

(HashSet으로 생각하시면 편하겠네요~)


c1과 c2에 들어오는 순서는 상관이 없습니다.
예를들어 mix(RED, YELLOW) 와 mix(YELLOW, RED) 는 둘다 ORANGE가 반환됩니다.

또한 else를 사용하여 남은 처리를 할수도 있습니다.

이렇게 when을 사용하면 if ~ else, switch를 대체하여 좀더 간결하고 직관적인 코드를 작성할 수 있습니다.

2.3.4 인자없는 when

위 경우에는 전부 when에 인자값을 넣어서 사용했으나, 인자값 없이도 사용할 수 있습니다.
단! when에 인자가 없다면, 조건부분은 반드시 Boolean을 반환하는 expression이어야 합니다.
fun mixOptimized(c1: Color, c2: Color) =
    when {
        (c1 == RED && c2 == YELLOW) ||
        (c1 == YELLOW && c2 == RED) ->
            ORANGE

        (c1 == YELLOW && c2 == BLUE) ||
        (c1 == BLUE && c2 == YELLOW) ->
            GREEN

        (c1 == BLUE && c2 == VIOLET) ||
        (c1 == VIOLET && c2 == BLUE) ->
            INDIGO

        else -> throw Exception("Dirty color")
    }

fun main(args: Array) {
    println(mixOptimized(BLUE, YELLOW))
}

setof를 빼면서 불필요 객체 생성은 막았지만, 왠지 복잡해 보이는군요.

이전 코드가 가독성은 더 좋아 보입니다.^^a


2.3.5 스마트 캐스트 (smart cast)

코틀린의 장점중 하나는 스마트 캐스트를 지원한다는 점입니다.

스마트 캐스트란 object의 타입 확인과 변환을 한번에 해주는 기능입니다.

interface Expr                   // 아무 함수도 없는 Dummy interface를 만든다.
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

fun eval(e: Expr): Int {
    if (e is Num) {        // smart cast가 발생한다. 
        val n = e as Num   // e를 Num type으로 전환하지만, 이미 smart cast되어 Num 타입이다. (생략가능 라인)
        return n.value
    }
    if (e is Sum) {        // smart cast가 발생한다. 
        // 이미 e는 Sum type으로 변환된 상태이다 -> as를 이용하여 변환할 필요가 없다.
        return eval(e.right) + eval(e.left)  // 따라서 sum의 property인 right, left에 바로 접근할 수 있다.
    }
    throw IllegalArgumentException("Unknown expression")
}

fun main(args: Array) {
    println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
}


위 예제를 보면 NumSum은 모두 Expr 이라는 interface를 구현하고 있습니다.
is instanceof와 같은 역할이라고 보면 되며 as는 강제 캐스팅이라고 보면 됩니다.

위에서 if (e is Num) 을 통과하여 내부 블럭으로 들어왔다면 e as Num 을 해줄 필요가 없습니다.
사실 이미 내부 블럭에서 e는 컴파일러에 의해 Num으로 이미 자동 캐스팅된 상태입니다.


이 코드를 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")
    }

if를 써도 되지만 when을 쓰는게 좀더 간결합니다.


2.3.7 if문과 when에서의 블럭사용

if문과 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")
    }


반응형