[Kotlin] 3. 선택 표현과 처리: enum과 when
2023. 3. 22. 09:20ㆍProgramming Languages/Kotlin
1. enum 클래스
enum class Color {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
- Java에서는 enum 키워드를 쓰지만 Kotlin에서는 enum class 키워드로 enum 클래스를 만든다. Kotlin에서 enum은 소프트 키워드(soft keyword)라고 부른다.
- 소프트 키워드는 특정 상황에서만 키워드로 사용되는 키워드로 그 상황이 아니라면 일반적인 상황에서 사용할 수 있다.
⚠️class는 키워드이다. 따라서 class라는 이름은 사용할 수 없으므로 clazz, aClass와 같이 사용해야 한다.
Kotlin의 enum 또한 프로퍼티와 메서드를 정의할 수 있다.
enum class Color (
val r: Int, val g: Int, val b: Int
) {
RED(255, 0, 0), ORANGE(255, 165, 0),
YELLOW(255, 255), GREEN(0, 255, 0), BLUE(0, 0, 255),
INDIGO(75, 0, 130), VIOLET(238, 130, 238);
fun rgb() = (r * 256 + g) * 256 + b
}
println(Color.BLUE.rgb()) // 255
- 각 enum의 상수를 지정할 때는 그 상수에 해당하는 프로퍼티 값을 지정해야 한다.
⚠️Kotlin에서 유일하게 세미콜론이 필수인 부분이 이 경우이다. enum 클래스에서 메서드를 정의하는 경우 enum 클래스의 상수 목록과 메서드 사이에 반드시 세미콜론을 넣어야 한다.
2. when으로 enum 클래스 다루기
Java의 switch 문은 Kotlin에 when 식으로 사용할 수 있다. 하지만 switch 문보다 훨씬 강력하게 활용할 수 있다.
fun getMnemonic(color: Color) {
when (color) {
Color.RED -> "Richard"
Color.ORNAGE -> "Of"
Color.YELLOW -> "York"
Color.GREEN -> "Gave"
Color.BLUE -> "Battle"
Color.INDIGO -> "In"
Color.VIOLET -> "Vain"
}
}
println(getMnemonic(Color.RED)) // Richard
- break가 없어도 된다.
- 한 분기에 여러 값과 매칭할 수 있다. 이 경우 콤마(,)로 구분된다.
fun getWarmth(color: Color) = when(color) {
Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
Color.GREEN -> "neutral"
Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold"
}
println(getWarmth(Color.RED)) // warm
3. when과 임의의 객체를 함께 사용
Java에서 switch는 enum 상수 또는 숫자 리터럴만 사용할 수 있었지만 Kotlin에서는 임의의 객체를 허용한다.
import colors.Color
import colors.Color.*
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")
}
println(mix(BLUE, VIOLET) // INDIGO
위 같은 경우 setOf(c1, c2)가 when의 인자로 들어갔으며 각 분기에서는 해당 인자와 동등성(equality)을 검사한다. 즉 위의 when은 c1과 c2가 각 분기 조건에 맞는지를 확인하고 결과를 반환한다. 만약 어떤 조건도 맞지 않으면 else 분기가 실행된다.
4. 인자 없는 when
3의 경우 비교할 때마다 Set의 인스턴스를 생성한다는 점이 걸리는 사람도 있을 것이다. 앞 예제의 경우엔 큰 문제가 되지 않지만 이 함수가 자주 호출된다면 불필요한 가비지 객체가 늘어나기 때문에 함수를 고치는 것이 낫다. 인자가 없는 when 식을 사용하면 읽기는 조금 어려워지지만 성능이 향상된다.
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")
}
println(mixOptimized(BLUE, YELLOW)) // GREEN
5. 스마트 캐스트: 타입 검사와 타입 캐스트를 조합
어떤 변수가 원하는 타입인지 is로 검사하고 나면 굳이 변수를 해당 타입으로 변환하지 않고 컴파일러가 수행하는 기능이다.
Java Style
interface Expr
class Num(val value: Int): Expr
class Sum(val left: Expr, val right: Expr): Expr
fun eval(e: Expr): Int {
if (e is Num) {
val n = e as Num
return n.value
}
if (e is Sum) {
val s = e as Sum
return eval(s.left) + eval(s.right)
}
throw IllegalArgumentException()
}
Kotlin Style
interface Expr
class Num(val value: Int): Expr
class Sum(val left: Expr, val right: Expr): Expr
fun eval(e: Expr): Int {
when (e) { // if를 사용해도 스마트 캐스트가 똑같이 적용된다.
is Num -> e.value
is Sum -> eval(e.left) + eval(e.right)
else -> throw IllegalArgumentException()
}
}
⚠️스마트 캐스트는 is로 변수에 든 값의 타입을 검사한 후 그 값이 바뀔 수 없는 경우에만 작동한다. 즉 클래스의 프로퍼티에 사용한다면 해당 프로퍼티는 val이어야 하고 커스텀 접근자를 사용하지 않았어야 한다. 커스텀 접근자를 구현했다면 게터를 통해 접근했을 때 어떤 값이 반환될지 모르기 때문이다. 원하는 타입으로 변환할 땐 as 키워드를 사용한다.
6. if와 when의 분기에서 블록 사용
블록이 값을 만들어내야 하는 경우 블록의 마지막 식이 블록의 결과이다. 이는 함수에 대해서는 성립하지 않는다. 식이 본문인 함수는 블록을 본문으로 가질 수 없고 블록이 본문인 함수는 반드시 return 문이 있어야 한다.
interface Expr
class Num(val value: Int): Expr
class Sum(val left: Expr, val right: Expr): Expr
fun evalWithLogging(e: Expr): Int {
when (e) { // if를 사용해도 스마트 캐스트가 똑같이 적용된다.
is Num -> {
println(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()
}
}
fun compare(n1: Int, n2: Int): Int {
// 앞 숫자가 크면 1, 작으면 -1, 같으면 0을 반환하는 함수
// 해당 블록은 본문이므로 return이 필수이다.
return if (n1 > n2) 1 if (n1 < n2) -1 else 0
}
fun compareExpr(n1: Int, n2: Int) = if (n1 > n2) 1 if (n1 < n2) -1 else 0
'Programming Languages > Kotlin' 카테고리의 다른 글
[Kotlin] 6. 함수 정의와 호출 (0) | 2023.03.24 |
---|---|
[Kotlin] 5. 예외처리 (0) | 2023.03.23 |
[Kotlin] 4. 대상을 이터레이션: while 과 for (0) | 2023.03.23 |
[Kotlin] 2. 클래스와 프로퍼티, 소스코드 구조 (0) | 2023.03.21 |
[Kotlin] 1. 함수, 변수, 문자열 템플릿 (0) | 2023.03.21 |