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)
}
```