[Kotlin] 예외 처리
코틀린의 예외처리는 자바의 접근 방법과 아주 비슷하다. 함수는 정상 종료, 즉 어떤 값을 반환하거나 비정상적으로 오류가 발생한 경우 예외를 던질(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를 출력한다.
예외를 던질 때는 다음과 같은 일이 벌어진다.
- 프로그램은 예외를 잡아내는 핸들러(exception handler)를 찾는다. 예외와 일치하는 예외 핸들러가 있다면 예외 핸들러가 예외를 처리한다.
- 현재 함수 내부에서 핸들러를 찾을수 없으면 함수 실행이 종료되고 함수가 스택에서 제거된다. 그리고 호출한 쪽의 문맥 안에서 예외 핸들러가 검색을 수행한다. 이런 경우 예외를 호출자에게 전파(propagate)했다고 말한다.
- 프로그램 진입점에 이룰 때 까지 예외를 잡아내지 못하면 현재 스레드가 종료된다.
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 블록의 값에 영향을 미치지 못한다는 점에 유의해야한다.