Skils/Kotlin

[Kotlin] 예외 처리

재한 2022. 8. 27. 20:40

코틀린의 예외처리는 자바의 접근 방법과 아주 비슷하다. 함수는 정상 종료, 즉 어떤 값을 반환하거나 비정상적으로 오류가 발생한 경우 예외를 던질(thorw) 수 있다. 예외가 발생한 경우에는 함수를 호출한 쪽에서 예외를 잡아내거나(catch), 

(잡아내지 않는 경우) 함수 호출 스택의 위로 예외가 전달될 수 있다. 

 

자바와 마찬가지로 오류 조건을 신호로 보내려면 throw 식에 예외 객체를 사용해야 한다.

 

import java.util.*
import kotlin.*
fun main(){
    val str=readLine()!!.toString()
    println(parseIntNumber(str))
}
fun parseIntNumber(s: String): Int{
    var num=0
    if(s.length !in 1..31) throw NumberFormatException("Not a number :$s")

    for(c in s){
        if(c !in '0'..'1') throw NumberFormatException("Not a number :$s")
        num=num*2+(c-'0')
    }
    return num
}

위 코드는 문자열을 2진수로 파싱하는 코드인데 만약 2진수에서 허용되는 숫자(0,1)가 아니라면

오류를 발생시키고 Not a number : number를 출력한다.

 

예외를 던질 때는 다음과 같은 일이 벌어진다.

  1. 프로그램은 예외를 잡아내는 핸들러(exception handler)를 찾는다. 예외와 일치하는 예외 핸들러가 있다면 예외 핸들러가 예외를 처리한다.
  2. 현재 함수 내부에서 핸들러를 찾을수 없으면 함수 실행이 종료되고 함수가 스택에서 제거된다. 그리고 호출한 쪽의 문맥 안에서 예외 핸들러가 검색을 수행한다. 이런 경우 예외를 호출자에게 전파(propagate)했다고 말한다.
  3. 프로그램 진입점에 이룰 때 까지 예외를 잡아내지 못하면 현재 스레드가 종료된다.

 

try

코틀린에서 예외를 처리할 때는 기본적으로 자바와 똑같은 문법의 try문을 사용한다.

import java.util.*
import kotlin.*
import java.lang.*
fun main(){
    readInt(1)
}
fun readInt(default : Int):Int{
    try {
        return readLine()!!.toInt()
    }catch (e:NumberFormatException){
        return default
    }
}

위의 코드를 보면 알수 있듯이 예외가 발생할 수 있는 코드(여기서는 toInt() 호출)를 try블록으로 감싼다.

방금 본 첫 번째 형태의 try문에는 최소한 하나 이상 적절한 타입의 예외를 잡아내는 catch 블록이 있어야 한다.

처리할 예외는 예외 파라미터로 표현되며, catch 블록의 내부에서는 이 파라미터를 마음대로 쓸 수 있다. try 블록 내부의 코드가 예외를 던지면, 코드 실행이 중단되고 프로그램은 예외를 처리할 수 있는 첫 번째 catch블록으로 제어를 이동한다.

만약 예외아 일치하는 catch 블록이 없으면 예외가 전파된다.

 

cath블록은 선언된 순서대로 예외 타입을 검사하기 때문에 어떤 타입을 처리할 수 있는 catch블록을 그 타입의 상위 타입을 처리할 수 있는 catch 블록보다 앞에 작성해야 한다. 그렇지 않으면 상위 타입을 잡아내는 핸들러가 하위 타입인 예외도 모두 잡아내버린다. 다음 코드에서는 NumberFormatException은 Exception의 하위 타입이기 때문에 두 번째 캐치 블록은 죽어있는 코드이다.

import java.util.*
import kotlin.*
import java.lang.*
fun main(){
    readInt(1)
}
fun readInt(default : Int):Int{
    try {
        return readLine()!!.toInt()
    } catch(e:Exception){ return 0}
    catch (e:NumberFormatException){
        return default
    }
}

 

자바와 코틀린의 try문에서 가장 크게 다른 점은 코틀린 try가 식이라는 것이다. 

이 식의 값은 [예외가 발생하지 않은 경우] try 블록의 값이거나 예외를 처리한 catch 블록의 값이 된다.

 

 

try 문의 다른 형태는 finally 블록을 사용한다. finally 블록은 try 블록을 떠나기 전에 프로그램이 어떤 일을 수행하도록 만들어준다.

import java.util.*
import kotlin.*
import java.lang.*
fun main(){
    readInt(1)
}
fun readInt(default : Int):Int{
    try {
        return readLine()!!.toInt()
    } finally{
        println("Error")
    }
}

이러한 finally 블록은 try 블록 앞이나 내부에서 할당한 자원을 해제할 때 유용하다.

  • 파일을 닫는 일
  • 네트워크 연결을 닫는 일

그리고 catch와 finally를 한 try 문 안에서 함께 사용할 수 있다.

try 블록을 식으로 사용할 경우, finally 블록의 값은 전체 try 블록의 값에 영향을 미치지 못한다는 점에 유의해야한다.