enum

자바처럼 ``kt enum`` 클래스 안에 프로퍼티나 메소드를 정의할 수 있다.

메소드를 정의하는 경우  상수와 메소드 사이에 반드시 `` ;``를 적어주어야 한다.

```kt

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

}

>>> Color.RED.ordinal    // index

0

>>> Color.RED.r

255

```

 

enum은 class 상속은 불가하고 interface 구현은 가능한데, interface가 필드 가지도록 해서 아래와 같은 코드를 인터페이스에 일원화 할 수 있다.

```kt

interface AddrInterface {

    val real: String

    val test: String

 

    fun getAddr(chain: Chain): String {

        return when (chain) {

            Chain.REAL -> real

            Chain.TEST -> test

        }

    }

}

```

참고로 이런 경우 Address라는 {real, test}를 가지고 있는 타입을 따로 정의하고 메서드를 Address에 위치시키는 것도 방법이다.

 

when

``c switch`` 대신 ``kt when``을 사용하며 더 강력하다. ``kt when``도 식이다.
```kt
import colors.Color
import colors.Color.*    // enum class 내부 프로퍼티를 이런 식으로 import 가능.
 
fun getWarmth(color: Color)  = when (color) {
    RED, ORANGE, YELLOW -> "warm"
  GREEN -> "neutral"
  BLUE, INDIGO, VIOLET -> "cold"
}
```
 
분기 조건에 상수만 사용할 수 있는 자바의 ``c switch``와 달리 ``kt when``은 분기 조건에 임의 객체를 사용할 수 있다.
```kt
fun mix(c1: Color, c2: Color) = 
    when (setOf(c1, c2)) {    // 집합을 인자로 받는다.
      setOf(RED, YELLOW) -> ORANGE
      setOf(BLUE, VIOLET) -> INDIGO
      else -> throw Exception("Dirty color")
  }
```
 
``kt setOf()``가 반복적으로 호출되는게 마음에 들지 않는다면, 다음과 같이 인자 없는 ``kt when``을 사용해 가독성에 손해를 보는 대신 성능을 올릴 수 있다.
Note ) 인자 없는 ``kt when``을 사용하려면 각 분기 조건은 반드시 불리언 결과를 계산하는 식이어야 한다.
```kt
fun mixOpt(c1: Color, c2: Color): String {
    when {
        (c1 == RED && c2 == YELLOW) || 
        (c1 == YELLOW && c2 == RED) -> return "ORANGE"
        (c1 == BLUE && c2 == VIOLET) ||
        (c1 == VIOLET && c2 == BLUE) -> return "INDIGO"
        else -> throw Exception("Dirty color")
    }
}
```
 

 

sealed와 중첩 클래스 : sealed 안의 모든 경우의 수에 대해 분기 처리하도록 강제, 실수 방지

다음과 같은 경우 ``kt when``에는 반드시 ``kt else``(디폴트 분기)를 적어주어야 한다.
이렇게 디폴트 분기를 사용하는 경우 `` Expr`` 클래스 계층에 새로운 하위 클래스를 추가했을 때, ``kt when``에 추가하는 것을 깜빡해도 디폴트 분기를 타게 되므로 프로그램이 예상대로 동작하는 것 처럼 보여 이를 잡아내기가 어려워질 수 있다. 
```kt
interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr
 
fun eval(e: Expr): Int =
        when (e) {
            is Num -> e.value
            is Sum -> eval(e.right) + eval(e.left)
            else -> throw IllegalArgumentException("Unknown expresssion")
        }
```
이런 경우 ``kt sealed``와 중첩 클래스를 사용하면 디폴트 분기를 사용하지 않고 모든 경우의 수를 처리하도록 강제할 수 있어 더 확실하게 검사할 수 있다.
``kt sealed`` class는 자동으로 ``kt open``이다.
```kt
sealed class Expr {
    class Num(val value: Int) : Expr()
    class Sum(val left: Expr, val right: Expr) : Expr()
}
 
fun eval(e: Expr): Int =
        when (e) {
            is Expr.Num -> e.value
            is Expr.Sum -> eval(e.right) + eval(e.left)
        }
```