프로그래밍 노트/Kotlin

[Kotlin] 코틀린(Kotlin) enum과 when

깡냉쓰 2020. 11. 9. 23:26
728x90
반응형

2.3 선택 표현과 처리: enum과 when

when 은 자바의 switch를 대치하되 훨씬 더 강력하며, 앞으로 더 자주 사용할 프로그래밍 요소이다.

2.3.1 enum 클래스 정의

enum class Color{
    RED, ORAGNE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}

코틀린에서 enum은 소프트 키워드(soft keyword)라 부르는 존재다. 반면 class는 키워드다. enum은 특별한 의미를 지니지만 다른 곳에서 사용할 수 있는 반면, class라는 이름은 다른곳에서 사용할 수 없다.

자바의 마찬가지로 enum 클래스 안에도 프로퍼티나 메소드를 정의할 수 있다.

enum class Color(val r: Int, val g: Int, val b: Int){
    // 각 상수를 생성할 때 그에 대한 프로퍼티 값을 지정한다.
    RED(255, 0, 0), ORAGNE(255, 165, 0), YELLOW(255. 255. 0), .... VIOLET(238, 130 ,238);

    fun rgb() = (r * 256 + g) * 256 + b // enum 클래스 안에서 메소드를 정의한다.
}

>>> println(Color.BLUE.rgb())

2.3.2 when으로 enum 클래스 다루기

fun getMnemonic(color: Color) = // 함수의 반환값으로 when 식을 직접 사용
    when (color) {
        Color.RED -> "Richard"
        Color.ORANGE -> "Of"
        ...
        Color.VIOLET -> "Vain"
    }

>> println(getMnemonic(Color.BLUE))
Battle

한 when 분기 안에 여러 값 사용하기

fun getWarmth(color: Color) = // 함수의 반환값으로 when 식을 직접 사용
    when (color) {
        Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
        Color.GREEN -> "neutral"
        ...
    }

>> println(getWarmth(Color.ORANGE))
warm

상수 값을 임포트하면 코드를 더 간단하게 만들 수 있다.

import ch2.colors.Color // 다른 패키지에서 정의한 Color 클래스를 임포트 한다.
import ch2.colors.Color.* // 짧은 이름으로 사용하기 위해 enum 상수를 모두 임포트 한다.

fun getWarmth(color: Color) = // 함수의 반환값으로 when 식을 직접 사용
    when (color) {
        RED, ORANGE, YELLOW -> "warm"
        GREEN -> "neutral"
        ...
    }

>> println(getWarmth(Color.ORANGE))
warm

2.3.3 when과 임의의 객체를 함께 사용

분기 조건에 상수(enum 상수나 숫자 리터럴)만을 사용할 수 있는 자바 switch와 달리 코틀린 when의 분기 조건은 임의의 객체를 허용한다.

두 색을 혼합한 색을 반환하는 함수를 작성하자.

fun mix(c1: Color, c2: Color) =
    when (setOf(c1, c2)){ // when 식의 인자로 아무 객체나 사용할 수 있다.
        setOf(RED, YELLOW) -> ORANGE,
        setOf(YELLOW, BLUE) -> GREEN,
        ...
        else -> throw Exception("Dirty Color") // 매치되는 분기 조건이 없으면 이 문장 실행
    }

이를 구현하기 위해 집합 비교 를 사용한다.

코틀린 표준 라이브러리에는 인자로 전달받은 여러 객체를 그 객체들을 포함하는 집합인 Set 객체로 만드는 setOf라는 함수가 있다. 객체 사이를 매치할 때 동등성(equality)을 사용한다.

2.3.4 인자 없는 when 사용

앞의 예제는 setOf 함수를 사용하여 Set 인스턴스를 계속 생성한다. 그렇기 때문에 많이 호출된다면 불필요한 가바지 객체가 늘어난다. 이럴때 인자가 없는 when 식을 사용하면 불필요한 객체 생성을 막을 수 있다. 단 코드가 약간 읽기 어려워 진다.. 성능 향상을 위해 이정도 비용은 감수해야 하는 ..

fun minxOptimized(c1: Color, c2: Color) =
    when {
        (c1 == RED && c2 == YELLOW) || (c1 == YELLOW && c2 == RED) -> ORANGE
        (c1 == YELLOW && c2 == BLUE) || (c1 == BLUE && c2 ==YELLOW) -> GREEN
        ...
        else -> throw Exception("Dirty color")
    }

when에 아무 인자도 없으려면 각 분기의 조건이 불리언 결과를 계산하는 식이어야 한다.

2.3.5 스마트 캐스트 : 타입 검사와 타입 캐스트를 조합

(1 + 2) + 4 와 같은 간단한 산술식을 계산하는 함수를 만들어 보자.

Num은 항상 말단(leaf) 노드이며, Sum은 자식이 둘 있는 중간(non-terminal)노드이다. Sum 노드의 두 자식은 덧셈의 두 인자다.

Expr 인터페이스를 선언해보자. 클래스가 구현하는 인터페이스를 지정하기 위해서 콜론(:) 뒤에 인터페이스 이름을 사용한다.

Expr은 아무 메소드도 선언하지 않으며, 단지 여러 타입의 식 객체를 아우르는 공통 타입 역할만 수행한다.

interface Expr
class Num(val value: Int) : Expr // value라는 프로퍼티만 존재하는 단순한 클래스로 Expr 인터페이스를 구현
class Sum(val left: Expr, val right: Expr) Expr // Expr 타입의 객체라면 어떤 것이나 Sum의 연산의 인자가 될 수 있다. 따라서 Num이나 다른 Sum이 인자로 올 수 있다.

>> println (eval(Sum(Sum(Num(1), Num(2)), Num(4)))
7

Expr 인터페이스에는 두 가지 구현 클래스가 존재한다. 따라서 식을 평가하려면 두 가지 경우를 고려해야 한다.

  • 어떤 식이 수라면 그 값을 반환한다.
  • 어떤 식이 합계라면 좌항과 우항의 값을 계산한 다음에 그 두 값을 합한 값을 반환한다.

코틀린에서 if 문을 사용하여 자바 스타일로 함수를 작성해보기

fun eval(e: Expr): Int{
    if(e is Num){
        val n = e as Num // 타입 변환(불필요한 중복)
        return n.value
    }

    if (e is Sum){
        return eval(e.right) + eval(e.left) // 변수 e에 대해 스마트 캐스트를 사용
    }
    throw IllegalArgumentException("Unknown expression")
}

코틀린의 is 는 자바의 instanceof와 비슷하다. 자바에서는 instanceOf로 확인한 다음에 그 타입에 속한 멤버에 접근하기 위해서는 명시적으로 변수 타입을 캐스팅해야 한다. 코틀린에서는 프로그래머 대신 컴파일러가 캐스팅을 해준다. is로 검사하고 나면 컴파일러가 캐스팅을 수행해주는데 이를 스마트 캐스트(smart cast)라고 부른다.

2.3.6 리팩토링: if를 when으로 변경

if식을 본문으로 사용해 더 간단한 메소드를 만들 수 있다.

fun eval(e: Expr): Int = 
    if(e is Num){
        e.value
    }else if(e is Sum){
        eval(e.right) + eval(e.left)
    }else{
        throw IllegalArgumentException("Unknown expression")
    }

>>> println(eval(Sum(Num(1), Num(2)))
3

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")
    }
728x90
반응형