Android

[Android] 코틀린(Kotlin) null과 예외(Exception)

구루싸 2020. 4. 18. 17:05
반응형
SMALL

오늘도 넷플릭스로 플래쉬(Flash)를 시청하면서

코틀린(Kotlin) 학습을 이어가겠습니다

주제는 제목처럼 코틀린의 null과 예외(Exception)처리입니다

null은 var이나 val 변수의 값이 없다는 것을 나타내는 값인데

자바를 포함해서 많은 프로그래밍 언어에서

null은 프로그램이 중단되는 크래시(crash)를 유발하는 원인이 됩니다

이유는 컴파일 시점에 에러를 알 수 없기 떄문인데

코틀린은 null 가능 타입과 불가능 타입을 구분하여

위험을 사전에 방지할 수 있습니다

fun main(args: Array<String>) {
    var beverage = readLine()?.capitalize()
    println(beverage)
}

위의 코드에서 readLine() 다음에 ?를 붙이지 않는다면 에러가 발생합니다

이유는 readLine() 함수의 반환 타입이 String? 로

null이 가능하기 때문인데 이를 컴파일이나 런타임 에러 없이

항상 실행되도록 해야하는 경우에는

안전 호출 연산자(safe call operator)인 ?를 붙입니다

이렇게 하면 readLine()의 반환값이 null이 아닌 경우에만

capitalize()가 안전하게 호출됩니다

fun main(args: Array<String>) {
    var beverage = readLine()?.let{
        if (it.isNotBlank()) {
            it.capitalize()
        } else {
            "맥주"
        }
    }
    println(beverage)
}

위의 코드는 let과 ?를 이용하여

안전하게 null을 처리하는 방법입니다

fun main(args: Array<String>) {
    var beverage = readLine()!!.capitalize()
    println(beverage)
}

반면에 non-null 단언 연산자(assertion operator)인 !!도 있는데

위의 코드처럼 사용하면 readLine()의 결과가 null이어도

capitalize()를 실행하므로 런타임에 Exception이 발생할 수 있습니다

(콘솔 입력 시에 Ctrl + D를 누르면 null이 발생됩니다)

fun main(args: Array<String>) {
    var beverage = readLine()

    if (beverage != null) {
        beverage = beverage.capitalize()
    } else {
        println("beverage is null")
    }

    val beverageServed: String = beverage ?: "beer"
    println(beverageServed)
}

위의 코드는 if 문으로 null 여부를 체크하고

beverageServed 변수에 beverage가 null이면 "beer"를

null이 아니면 beverage 값을 넣습니다

이 때 ?: 는 null 복합 연산자(null coalescing operator)라고 하고

이를 이용해서 위의 코드를 아래와 같이 줄일 수 있습니다

fun main(args: Array<String>) {
    var beverage = readLine()
    beverage?.let {
        beverage = it.capitalize()
    } ?: println("beverage is null")

    val beverageServed: String = beverage ?: "beer"
    println(beverageServed)
}

다음으로 예외(Exception) 처리에 대해 알아보겠습니다

import java.lang.Exception
import java.lang.IllegalStateException

fun proficiencyCheck(swordsJuggling: Int?) {
    //swordsJuggling ?: throw UnskilledSwordJugglerException()
    /* precondition function */
    checkNotNull(swordsJuggling, { "Player cannot juggle" })
}
class UnskilledSwordJugglerException() : IllegalStateException("Player cannot juggle")
fun main(args: Array<String>) {
    var swordsJuggling: Int? = null
    var isJugglingProficient = (1..3).shuffled().last() == 3
    if (isJugglingProficient) {
        swordsJuggling = 2
    }
    try {
        proficiencyCheck(swordsJuggling)
        swordsJuggling = swordsJuggling!!.plus(1)
    } catch (e: Exception) {
        println(e)
    }
    if (swordsJuggling != null) {
        if (swordsJuggling == 1) {
            println("Juggle with $swordsJuggling knife")
        } else {
            println("Juggle with $swordsJuggling knives")
        }
    } else {
        println("No knife")
    }
}

위의 코드를 보면 try문 안에서 proficiencyCheck() 함수를 호출합니다

이 함수는 파라미터로 받은 값의 null 여부를 확인해서

null일 경우 커스텀(custom) 예외로 IllegalStateException를 상속한

UnskilledSwordJugglerException에 예외를 던지고

이 클래스에서 IllegalStateException을 발생시킵니다

본래는 크래쉬(crash)가 발생해야하지만

try 문을 통해 예외처리를 하였으므로

catch문 안에 println()에 의해 "Player cannot juggle"이 출력되고

아래의 No knife를 출력하고 종료됩니다

proficiencyCheck() 함수를 보시면 checkNotNull() 함수가 있는데요

(주석 처리부분과 동일한 결과)

이는 전제 조건 함수(precondition fuction)이라고해서 

코틀린이 제공하는 표준 라이브러리 함수합니다

함수 설명
checkNotNull 첫번째 인자값이 null이면 IllegalStateException을 발생시키며, 그렇지 않으면 첫번째 인자값을 반환
require 첫번째 인자값이 false면 IllegalArgumentException을 발생시킴
requireNotNull 첫번째 인자값이 null이면 IllegalArgumentException을 발생시키며, 그렇지 않으면 첫번째 인자값을 반환
error 첫번째 인자값이 null이면 제공된 메시지와 함께 IllegalStateException을 발생시키며, 그렇지 않으면 첫번째 인자값을 반환
assert 인자값이 false면 AssertionError를 발생시키고 컴파일러의 assertion flag가 활성화됨

코틀린에서는 모든 예외가 unchecked 예외로

예외가 생길 수 있는 모든 코드를 try/catch 문으로

반드시 처리하도록 컴파일러가 강요하지 않습니다

 참고로 checked 예외는 처리하지 않을 경우 컴파일 오류가 발생합니다

이것으로 이번 학습을 마치겠습니다

그럼 이만-_-

 

 

반응형
LIST