[Kotlin in Action] 4. enum 과 when, 클래스 계층과 스마트 캐스트

반응형
728x90
반응형

enum

enum은 class 앞에 있을때는 특별한 의미를 지니지만, 다른 곳에서는 이름에 사용할 수 있다.

이를 소프트 키워드(soft keyword)라고 부른다.

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

 

이름에 사용할 수 있다?

예를들어 class는 키워드다. 

class는 이름 자체로 사용할 수 없어서, class 라는 이름을 사용하려면 clazz 또는 aClass 등과 같이 사용해야한다.

 

 

enum 예제 구현해보기

enum class Color2 (
    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); // ; 을 여기서 꼭 사용해야한다.

    // enum 클래스 안에서 함수를 정의한다.
    fun rgb() = (r * 256 + g) * 256 + b
}
  • 상수의 프로퍼티를 정의한다.
enum class Color2 (
    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); // ; 을 여기서 꼭 사용해야한다.

 

  • 세미콜론(;)을 꼭 사용해야한다.
INDIGO(75, 0, 130), VIOLET(238, 130, 238); // ; 을 여기서 꼭 사용해야한다.

 

 

when

  • 함수의 반환값으로 when 식을 직접 사용한다.
// 함수의 반환값으로 when 식을 직접 사용한다.
fun getMnemonic(color: Color2) =
    when (color) { // 색이 특정 enum 상수와 같을때 그 상수에 대응하는 문자열을 돌려준다.
        // break 문을 쓰지 않아도 된다.
        RED -> "Richard"
        ORANGE -> "Of"
        YELLOW -> "York"
        GREEN -> "Gave"
        BLUE -> "Battle"
        INDIGO -> "In"
        VIOLET -> "Vain"
    }

 

  • 한 when 분기 안에 여러 값을 사용할 수 있다.
// 한 when 분기 안에 여러 값을 사용할 수 있다.
fun getWarmth(color: Color2) =
    when (color) {
        RED, ORANGE, YELLOW -> "warm"
        GREEN -> "natural"
        BLUE, INDIGO, VIOLET -> "cold"
    }

 

  • when 식의 인자를 아무 객체나 사용할 수 있다.
setOf 함수
- 인자로 전달받은 여러 객체를 그 객체들을 포함하는 집합인 Set 객체로 만드는 코틀린 표준 라이브러리집합
- set은 원소가 모여있는 컬렉션으로, 각 원소의 순서는 중요하지 않다.
- SetOf(c1, c2) 와 setOf(RED, YELLOW) 가 같다는 말은 c1이 RED고, c2가 YELLOW 또는 c1이 YELLOW, c2 RED인 경우다.
fun mix(c1 : Color2, c2: Color2) {
        // when 식의 인자를 아무 객체나 사용할 수 있다.
        // when 은 이렇게 인자로 받은 객체가 각 분기 조건에 있는 객체와 같은지 테스트한다.
        when (setOf(c1, c2)) {
            setOf(RED, YELLOW) -> ORANGE
            setOf(YELLOW, BLUE) -> GREEN
            setOf(BLUE, VIOLET) -> INDIGO
            else -> throw Exception("Dirty color") // 매치되는 분기 조건이 없으면 이 문장을 실행한다.
        }
    }

 

  • 인자 없는 when 을 사용할 수 있다.
    • when에 아무 인자도 없으려면 각 분기의 조건이 boolean 결과를 계산하는 식이여야한다.
// 인자없는 when 사용
fun mixOptimized(c1 : Color2, c2 : Color2) =
    // when에 아무 인자도 없으려면 각 분기의 조건이 boolean 결과를 계산하는 식이여야한다.
    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") // 매치되는 분기 조건이 없으면 이 문장을 실행한다.
    }

 

 

클래스 계층

  • 식을 위한 Expr 인터페이스
  • Sum, Num 클래스가 Expr 인터페이스를 구현한다.
  • Expr 인터페이스는 아무 메서드도 선언하지 않으며, 단지 여러 타입의 식 객체를 아우르는 공통 타입 역할만 수행한다.
interface Expr

class Num(val value: Int) : Expr
// Expr 타입의 객체라면 어떤 것이나 Sum 연산의 인자가 될 수 있다. 따라서 Num 이나 다른 Sum이 인자로 올수있다.
class Sum(val left: Expr, val right: Expr) : Expr

 

 

스마트 캐스트

  • is 를 사용하여 변수 타입을 검사한다. (=java의 instanceOf 와 비슷하다.)
  • 코틀린에서는 프로그래머 대신 컴파일러가 캐스팅을 해준다.
  • 어떤 변수가 원하는 타입인지 일단 is로 검사하고 나면 굳이 변수를 원하는 타입으로 캐스팅하지 않아도, 마치 처음부터 그 변수가 원하는 타입으로 선언된 것처럼 사용할 수 있다.
fun eval(e: Expr): Int {
    if (e is Num) {
        // e 의 타입을 컴파일러는 Num 으로 해석한다.
        val n = e as Num // 여기서 Num 으로 타입을 변환하는데 이는 불필요한 중복이다.
        return n.value
    }

    if (e is Sum) {
        // e 의 타입을 컴파일러는 Sum 으로 해석한다.
        return eval(e.right) + eval(e.left) // 변수 e에 대해 스마트 캐스트를 사용한다.
    }

    throw IllegalArgumentException("Unknown expression")
}

1) e의 타입을 컴파일러는 Num으로 해석한다.

if (e is Num) {

2) e의 타입을 컴파일러는 Sum으로 해석한다.

if (e is Sum) {

 

  • 위 코드를 when 으로 전환해보자.
fun evalWhen(e: Expr) : Int =
    when(e) {
        is Num -> e.value
        is Sum -> evalWhen(e.right) + evalWhen(e.left)
        else -> throw IllegalArgumentException("Unknown expression")
    }

 

  • main 메서드를 선언해보자.
fun main() {
    println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
}

 

  • 위 main 메서드의 실행을 로깅해보자.
fun main() {
    println(evalWithLogging(Sum(Sum(Num(1), Num(2)), Num(4))))
}

 

* 식이 본문인 함수 : 블록을 본문으로 가질 수 없다.
* 블록이 본문인 함수 :  return 문이 반드시 존재해야한다.
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")
    }

 

 

 

반응형

Designed by JB FACTORY