[코인액] 코틀린 인 액션 #2

Yerin·2023년 1월 29일

코틀린

목록 보기
4/11
post-thumbnail

어제에 이어 오늘도.. 코틀린 인 액션을 공부해보자 (벌써 지겨운건 비☆밀)

선택 표현과 처리 : enum과 when

enum class 정의

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

위는 간단한 enum 클래스이다. 코틀린에서 enum은 소프트 키워드(soft keyword)라고 불린다.

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);
    
    // enum 클래스 안에서 메서드를 정의
    fun rgb() = (r*256 + g) * 256 +b
}

각 enum 상수를 정의할 때는 그 상수에 해당하는 프로퍼티 값을 지정해야만 한다.
enum 클래스 안에서 메서드를 정의하는 경우 반드시 enum 상수 목록과 메서드 정의 사이에 세미콜론을 넣어야 한다.

when으로 enum 클래스 다루기

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

if와 마찬가지도 when 또한 값을 만들어내는 식이다.
위의 예제는 함수의 반환 값으로 when 식을 집접 사용한다.
한 분기 안에서 여러 값을 매치 패턴으로 사용할 수 있으며, 이 경우 값 사이를 콤마로 분리한다.

import colors.Color
import colors.Color.*

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

상수 값을 임포트 하면 클래스 이름을 생략하여 더 간단한 코드로 작성할 수 있다.


when과 임의의 객체를 함께 사용하는 경우

fun mix(c1: Color, c2: Color) = 
	when (setOf(c1, c2)) {
    	setOf(RED, YELLOW) -> ORANGE
        setOf(YELLOW, BLUE) -> GREEN
        setOf(BLUE< VIOLET) -> INIDGO
        else -> throw Exception("Dirty Color")
    }

코틀린 when의 분기 조건은 상수 뿐만이 아니라 임의의 객체를 허용한다.

위의 함수는 호출 시 마다 함수 인자로 주어진 두 색이 when의 분기 조건에 있는 다른 두 색과 같은지 비교하기 위해 여러 Set 인스턴스를 생성한다. 보통은 이런 비효율성이 크게 문제가 되지 않지만, 이 함수가 아주 자주 호출될 경우 불필요한 가비지 객체가 늘어나는 것을 방지하기 위해 함수를 고쳐쓰는 편이 좋다. 인자가 없는 when식을 사용하면 불필요한 객체 생성을 막을 수 있다.


인자 없이 when을 사용하는 경우

fun mixOptimized(c1: Color, c2: Color) = 
	when {
    	(c1 == RED && c2 == YELLOW) ||
        (c1 == YELLOW && c2 == RED) ->
        	ORANGE
        (c1 == BLUE && c2 == YELLOW) ||
        (c1 == YELLOW && c2 == BLUE) ->
        	GREEN
        (c1 == BLUE && c2 == VIOLET) ||
        (c1 == VIOLET && c2 == BLUE) ->
        	INDIGO
        else -> throw Exception("Dirty Color")
    }

위와 같이 코드는 약간 가독성이 떨어지지만 성능을 더 향상시ㅋ기 위해 그 정도 비용을 감수해야 하는 경우도 자주 있다. when에 아무 인자도 없으려면 각 분기의 조건이 Boolean 결과를 계산하는 식이어야 한다.

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

Expr Interface : 아무 메서드도 선언하지 않으며, 단지 여러 타입의 식 객체를 아우르는 공통 타입 역할만 수행한다.
Expr 타입의 객체라면 어떤 것이나 Sum의 연산자가 될 수 있다.

interface Expr
Class Num(val value: Int): Expr
Class Sum(val left: Expr, val right: Expr): Expr

자바에서는 어떤 변수의 타입을 instanceof로 확인한 다음에 그 타입에 속한 멤버에 접근하기 위서는 명시적으로 변수 타입을 캐스팅해야한다. 이런 멤버 접근을 여러번 수행해야 한다면 변수에 따로 캐스팅한 결과를 저장한 후에 사용해야 한다.

코틀린에서는 프로그래머 대신 컴파일러가 캐스팅을 해준다. 어떤 변수가 원하는 타입인지 일단 is로 검사하고 나면 굳이 변수를 원하는 타입으로 캐스팅하지 않아도 마치 처음부터 그 변수가 원하는 타입으로 선언된 것 처럼 사용할 수 있다. 실제로는 컴파일러가 캐스팅을 수행해 주는 것인데 이를 스마트 캐스트 (smart cast)라고 부른다.

스마트 캐스트는 is로 변수에 든 값의 타입을 검사한 후 그 값이 바뀔 수 없는 경우에만 작동한다. 클래스의 프로퍼티에 대해 스마트 캐스트를 사용한다면 그 프로퍼티는 반드시 val이어야 하며 커스텀 접근자를 사용한 것이어도 안 된다. 원하는 타입으로 명시적으로 타입 캐스팅 하려면 as 키웓를 사용한다.

다음 예제를 확인해 보자

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

if문이 아니라 when을 사용해서도 eval을 더 간단하게 작성할 수 있다. (이거는 생략)


iteration

while

while (조건) {
	/* ... */	
}

do {
	/* ... */	
} while (조건)

코틀린의 while과 do-while은 어렵지 않기 때문에 넘어가도록 하자.

for

범위를 표현하는 방법은 다음과 같다.

// 1 이상, 100 이하
for (i in 1..100) 
// 1 이상, 100 이하, 2씩 건너 뛰기
for (i in 1..100 step 2) 
// 역순으로 2씩 뛰어 내림차순
for (i in 100 down to 1 step 2)
for(i in 1..100 step -2)
// 1이상, 99 이하
for (i in 1 until 100)
// map에서 key와 value를 순서대로
for ((key, value) in mapExample)
// list에서 index와 함께 value 값을
for ((index, element) in list.withIndex())

예외 처리

함수는 정상적으로 종료할 수 있지만, 오류가 발생하면 예외를 던질 수 (throw) 있다. 함수를 호출하는 쪽에선느 그 예외를 자바 처리할 수 있다. 발생한 예외를 함수 호출 단에서 처리(catch)하지 않으면 함수 호출 스택을 거슬러 올라가면서 예외를 처리하는 부분이 나올 때까지 예외를 다시 던진다(rethrow).

자바와 코틀린의 가장 큰 차이는 throws 절이 코드에 없다는 점이다. 자바에서는 함수 작성 시 함수 선언 뒤 throws IOException을 붙여야 한다. 그 이유는 IOException이 체크 예외이기 때문이다. 자바에서는 체크 예외를 명시적으로 처리해야 한다. - 어떤 함수가 던질 가능성이 있는 예외나 그 함수가 호출한 다른 함수에서 발생할 수 있는 예외를 모두 catch로 처리해야 하며, 처리하지 않은 예외는 throws절에 명시해야 한다.

코틀린에선느 (다른 최신 JVM언어와 마찬가지로) 체크 예외와 언체크 예외를 구분하지 않는다. - 함수가 던지는 예외를 지정하지 않고 발생한 예외를 잡아내도 되고 잡아내지 않아도 된다. (간단히 말해 함수가 던질 수 있는 예외를 선언하지 않아도 된다!)


여기까지가 2장의 내용이다. 다소 지루한 내용이지만.. 아는 것을 배울 때 많이 웃어두자. 하하하 !


profile
𝙸 𝚐𝚘𝚝𝚝𝚊 𝚕𝚒𝚟𝚎 𝚖𝚢 𝚕𝚒𝚏𝚎 𝙽𝙾𝚆, 𝙽𝙾𝚃 𝚕𝚊𝚝𝚎𝚛 !

0개의 댓글