본문 바로가기
2023년 이전/kotlin

코틀린(Kotlin)- 선택 표현과 처리

by JeongUPark 2019. 10. 11.
반응형

[출처 -  Kotlin In Action] [아래 내용들은 Kotlin In Action을 공부하면서 스스로 정리한 내용입니다] 

 

이번에는 자바의 switch를 대신하는 when과 enum을 선언하는 방법, 그리고 스마트 캐스트에 대해서 알아보겠습니다.

 

enum 클래스 정의

 

enum은 Java보다 선언에 더 많은 키워드를 써야하는 흔치 않은 예입니다. Java의 경우에는 enum으로 끝나는데, kotlin은 enum class로 선업합니다. enum의 선언은 다음과 같습니다.

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

위처럼 단순하게 나열 하는것 뿐많 아니라

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
}
fun main(args: Array<String>) {
    println(Color.BLUE.rgb())
}

위의 방식처럼 프로퍼티나 메소드를 enum 안에 선언 할 수 있습니다. 위 처럼 선언할 경우 아래 println에서는 BLUE의 값인 r=0, g= 0, b = 255를 rgb 함수의 식으로 계산해서 값을 획득 할 수 잇습니다. (결과 값은 255 입니다.)

 

when

 

when은 처음에 언급했듯이 Java의 switch를 대신합니다.

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

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

위의 code는 getMnemonic에 전달 받은 color enum이 아래 when의 블럭안에 있는 값들중 일치하는 값을 print 하는 code입니다.

즉 when은

when(분기조건){ 
 조건1,조건2,조건3 -> 동작,
 조건4 -> 동작,
 조건5,조건6 -> 동작
}

//java로 처리할 경우
if(분기조건 == 조건1 || 분기조건 == 조건2 || 분기조건 ==조건3){
  동작
}else if(분기조건 == 조건4){
	동작
}else if(분지조건 == 조건5 || 분기조건 == 조선6){
	동작
}
// 또는
switch(분기조건){
	case 조건1:
    case 조건2:
    case 조건3:
        동작; break;
    case 조건4:
    	동작; break;
    case 조건5:
    case 조건6:
        동작; break;
}

분기조건 값이 아래 조건들 중에 true를 반환하는 (같거나 크거나 등등) 조건의 동작을 수행합니다. 이때 동작이 1line으로 처리 안될 경우 

when(분기조건){  
 조건1 -> {다양한 동작}
 else -> 동작
 }

블럭 처리하여 처리할 수 있으며, else를 사용하여 조건 외의 처리를 할 수 있습니다.

그리고 when은 switch와 다르게 임의의 객체로 분기 조건을 설정할 수 있습니다. (Java의 switch는 enum 상수나 숫자 리터럴만 가능)

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

fun main(args: Array<String>) {
    println(mix(BLUE, YELLOW))
}

 위의 setOf는 kotlin에서 제공하는 함수로, 원소가 모여있는 컬렉션으로 동일한 원소들이 존재하는 setOf가 있을 경우 동일로 인식 합니다. 아무튼 위의 code 처럼 when은  enum 상수나 숫자 리터럴 이외에도 임의의 객체로 분기 조건으로 설정할수있습니다.

마지막으노 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")
    }

fun main(args: Array<String>) {
    println(mixOptimized(BLUE, YELLOW))
}

 

스마트 캐스트 : 타입 검사와 타입 캐스트를 조합

java instanceof 나 타입 캐스팅을 kotlin에서 어떻게 하는지 알아보겠습니다.

 

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) {
        return eval(e.right) + eval(e.left)
    }
    throw IllegalArgumentException("Unknown expression")
}

fun main(args: Array<String>) {
    println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
}

위의 code를 보면 is와 as가 있습니다. is는 Java instanceof이고 as는 Java의 타입 캐스팅 입니다.

그래서 Num이면 숫자를 반환하고 Sum이면 덧셈을 합니다. 위 code의 진행을 보면

Num(1), Num(2)를 통하여 1과 2를 return 받고 -> Sum(1, 2) 가 되어 1+2를 해서 3을 return 합니다. ->Num(4)를 통하여 4를 return 합니다. -> 그렇게 Sum(Sum(Num(1), Num(2)), Num(4)))는 (1+2)+4의 결과 값이 7을 return 합니다.

 

그리고 is가 true이고 그 데이터를 사용한다면 as를 할 필요가 없습니다. 어떻말이냐면

if (e is Num) {
    val n = e
    return n.value
}

일 경우 n은 알아서 Num으로 캐스팅 됩니다. 그렇기 때문에 code를 더 간단하게 변경하면

if (e is Num) {
   return e.value
}

로 변경할 수 있습니다.


if와 when을 사용하여 위의 code를 변경하면

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) {
        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(Num(1), Num(2))))
}
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 expression")
    }

fun main(args: Array<String>) {
    println(eval(Sum(Num(1), Num(2))))
}

 if의 경우에는 fun eval(e:Expr) : Int = 를 하여 조건문에 맞는 결과 값을 return 할 수 있고, 

when의 경우에는 조건에 is를 사용하여 결과를 return 받을 수 있습니다.

반응형