KotlinInAction/코틀린 기초

enum과 when

webmaster 2022. 7. 25. 20:54
728x90

Enum

enum class Color {
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
  • Enum은 자바 선언보다 코틀린 선언에 더 많은 키워드를 써야 한다
  • 코틀린에서는 enum class를 사용한다.
  • 코틀린에서 enum은 소프트 키워드 라 부른다.
    • enum은 class 앞에 있을 때는 특별한 의미를 지니지만 다른 곳에서는 이름에 사용할 수 있다.

Enum 클래스 안에 프로퍼티와 메서드 쓰기

enum class Color(var 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;
}
fun main(args: Array<String>) {
    println(Color.BLUE.rgb())
}
  • 일반적인 클래스와 마찬가지로 생성자와 프로퍼티를 선언한다.
  • 각 enum 상수를 정의할 때는 그 상수에 해당하는 프로퍼티 값을 지정해야만 한다.
  • 세미콜론이 필수로 들어가야 한다.
    • enum 클래스 안에서 메서드를 정의하는 경우 반드시 enum 상수 목록과 메서드 정의 사이에 세미콜론을 넣어야 한다

When으로 enum 클래스 다루기

자바의 switch에 해당하는 코틀린 구성요소가 when이다.

fun getMnemonic(color: Color) =
    when(color){
        Color.RED -> "Richard"
        Color.ORANGE -> "Of"
        Color.YELLOW -> "York"
        Color.GREEN -> "Gave"
        Color.BLUE -> "Battle"
        Color.INDIGO -> "In"
        Color.VIOLET -> "Vain"
    }
fun main(args: Array<String>) {
    println(getMnemonic(Color.BLUE))
}
  • color로 전달된 값과 같은 분기를 찾는다.
  • 각 분기의 끝에 break를 넣지 않아도 된다.
  • 성공적으로 매치되는 분기를 찾으면 switch는 그 분기를 실행한다.
  • 한 분기 안에서 여러 값을 매치 패턴으로 사용할 수도 있다.
    • 값 사이를 콤마(,)로 분리한다.
fun getWarmth(color: Color) =
    when(color){
        Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
        Color.GREEN -> "neutral"
        Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold"
    }
fun main(args: Array<String>) {
    println(getWarmth(Color.ORANGE))
}

더 간단하게 만들기

import Color.*
fun getWarmth(color: Color) =
    when(color){
        RED, ORANGE, YELLOW -> "warm"
        GREEN -> "neutral"
        BLUE, INDIGO, VIOLET -> "cold"
    }

When과 임의의 객체를 함께 사용

when의 분기 조건은 임의의 객체를 허용한다.

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(Color.BLUE, Color.YELLOW))
  • when 식의 인자로 아무 객체나 사용할 수 있다.
  • 코틀린 표준 라이브러리에는 인자로 전달받은 여러 객체를 그 객체들을 포함하는 집합인 Set객체로 만드는 setOf함수가 있다.
    • 집합은 원소가 모여 있는 컬랙션으로 각 원소의 순서는 중요하지 않다
  • when 식은 인자 값과 매치하는 조건값을 찾을 때까지 각 분기를 검사한다.
    • 여기서는 객체 사이를 매치할 때 동등성을 사용한다.
  • when 분기 조건 부분에 식을 넣을 수 있기 때문에 많은 경우 코드를 더 간결하고 아름답게 작성이 가능하다

인자 없는 when 사용

이전의 코드는 함수 인자로 주어진 두 색이 when의 분기 조건에 있는 다른 두 색과 같은지를 비교하기 위해 여러 Set인스턴스를 생성하는 단점이 있다.

이러한 함수가 자주 호출되면 불필요한 가비지 객체가 늘어나므로 고치는 것이 좋다

fun mixOptimized(c1:Color, c2: Color) =
    when {
        (c1 == RED && c2 == YELLOW) || (c2 == RED && c1 == YELLOW) ->
            ORANGE
        (c1 == YELLOW && c2 == BLUE) || (c2 == BLUE && c1 == YELLOW) ->
            GREEN
        (c1 == BLUE && c2 == VIOLET) || (c2 == VIOLET && c1 == BLUE) ->
            INDIGO
        else -> throw Exception("Dirty color")
    }
println(mixOptimized(Color.BLUE, Color.YELLOW))
  • when에 아무 인자도 없으려면 각 분기의 조건이 불리언 결과를 계산하는 식이어야한다.
  • 추가 객체를 만들지 않는다는 장점이 있지만 가독성이 떨어진다.

스마일 캐스트

트리구조의 SUM , NUM의 조합 구하기

interface Expr
class Num(val value: Int) : Expr //Value라는 프로퍼티만 존재하는 단순한 클래스로 Expr 인터페이스를 구현한다
//Expr 타입의 객체라면 어떤 것이나 sum 연산의 인자가 될 수 있다.
//따라서 Num이나 다른 Sum 인자가 올수 있다.
class Sum(val left: Expr, val right: Expr) : Expr
fun main(args: Array<String>) {
	println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
}
  • 식을 트리 구조로 저장한다
    • NUM 은 항상 말단 노드, Sum은 자식이 둘 있는 중간 노드이다
  • 식을 위한 인터페이스(Expr) 이 있고, Sum, Num 클래스는 그 Expr인터페이스를 구현한다.
  • Expr인터페이스는 아무 메서드도 선언하지 않으며, 단지 여러 타입의 식 객체를 아우르는 공통 타입 역할만 수행한다
  • 클래스가 구현하는 인터페이스를 지정하기 위해서 콜론(:) 뒤에 인터페이스 이름을 사용한다
  • Expr인터페이스에는 두 가지 구현 클래스가 존재하므로 2가지 경우를 고려해야 한다
    • 어떤 식이 수라면 그 값을 반환
    • 어떤 식이 합계라면 좌항과 우항의 값을 계산한 다음 두 값을 합한 값을 반환

코틀린에서 자바 형식으로 작성

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)    
    }
    throw IllegalArgumentException("Unknown expression")
}
  • 코틀린에서는 is를 사용해 변수 타입을 검사한다
    • 자바의 instanceof와 비슷하다
    • 자바에서는 instanceof로 확인 한 다음 그 타입에 속한 멤버에 접근하기 위해 명시적으로 변수 타입을 캐스팅해야 한다
  • 코틀린에서는 프로그래머 대신 컴파일러가 캐스팅을 해준다
    • is로 검사하고 나면 굳이 변수를 원하는 타입으로 캐스팅하지 않아도 마치 처음부터 그 변수가 원하는 타입으로 선언된 것처럼 사용 가능하다
    • 실제로는 컴파일러가 캐스팅해준 것 => 스마트 캐스트
  • 스마트 캐스트는 is로 변수에 든 값의 타입을 검사한 다음에 그 값이 바뀔 수 없는 경우에만 작동한다.
    • val 이 아니거나 val이지만 커스텀 접근자를 사용하는 경우에는 해당 프로퍼티에 대한 접근이 항상 같은 값을 내놓는다고 확신할 수 없기 때문

if를 When으로 변경

코틀린에서는 if가 값을 만들어 내기 때문에 3항 연산자가 따로 없다.

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")
    }
fun main(args: Array<String>) {
	println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
}
  • if의 분기에 식이 하나밖에 없다면 중괄호 생략 가능
  • if분기에 블록을 사용하는 경우 그 블록의 마지막 식이 그 분기의 결괏값이다.

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")    
    }    
}
  • is를 사용하게 된다면 스마트 캐스트가 이뤄지기 때문에 Num, Sum변수에 접근할 때 변수를 강제로 캐스팅할 필요가 없다
  • if나 when의 각 분기에서 수행해야 하는 로직이 복잡해지면 분기 본문에 블록을 사용할 수 있다.

if와 When의 분기에서 블록 사용

if와 When의 분기에서 블록 사용할 경우 마지막 문장이 블록 전체의 결과가 된데

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")
    }
fun main(args: Array<String>) {
	println(evalWithLogging(Sum(Sum(Num(1), Num(2)), Num(4))))
}
  • 블록의 마지막 식이 블록의 결과라는 규칙은 블록이 값을 만들어내야 하는 경우 항상 성립한다.
    • 이 규칙은 함수에 대해서는 성립하지 않는다.
    • 식이 본문인 함수는 블록을 본문으로 가질 수 없고 블록이 본문인 함수는 내부에 return 문이 반드시 있어야 한다
728x90

'KotlinInAction > 코틀린 기초' 카테고리의 다른 글

코틀린의 예외 처리  (0) 2022.07.26
While과 for 루프  (0) 2022.07.26
클래스와 프로퍼티  (0) 2022.07.25
함수와 변수  (0) 2022.07.25