Kotlin Exception Handling (try, catch, finally)

Exception Basics

Kotlin exception handling uses the try, catch, and finally keywords, just like Java.

The following is an example of handling a NumberFormatException.

try {
    val num = "abc".toInt()
} catch (e : NumberFormatException) {
    System.err.println(e)
}

Output:

java.lang.NumberFormatException: For input string: "abc"

Throwing an exception also uses throw, just like Java. As with creating class instances without new, new is not needed here either.

fun fibonacci(n: Int) {
    if (n < 0) throw IllegalArgumentException("Negative numbers cannot be specified.")
    // ...
}

Output:

java.lang.IllegalArgumentException: Negative numbers cannot be specified.

Catching Exceptions Is Not Required

In Java, a function that calls a method throwing a checked exception such as IOException had to either catch the exception with catch or declare throws IOException on its own function. Kotlin does not distinguish between checked and unchecked exceptions, so there is no need to declare throws IOException on a function. Whether to catch a thrown exception is up to the caller. Uncaught exceptions propagate to the caller’s function.

fun throwIoException() {
    throw IOException("Loading error!")
}

fun foo() {
    // This calls a function that throws IOException,
    // but this foo method does not need a throws declaration.
    throwIoException()
}

fun main() {
    try {
        foo()
    } catch (e : IOException) {
        System.err.println(e)
    }
}

try Can Be Used as an Expression

In Kotlin, try is also an expression. You can receive the result value from a try block directly in a variable or return it as a function return value.

In the example below, the result of String.toInt() executed in the try block is assigned to the num variable.

fun printIfInt(s: String) {
    val num = try {
        s.toInt()
    } catch (e: NumberFormatException){
        return
    }
    println(num)
}

In Java, to reference an object created inside a try block from outside the block, the variable had to be defined outside the block first, for example by assigning null. Kotlin does not require that kind of cumbersome work.

In the example above, when an exception occurs, the function exits immediately with return, but it can also return another value like a ternary operator. In the following example, if the string cannot be parsed properly, null is stored in the num variable.

val num = try {
    s.toInt()
} catch (e: NumberFormatException){
    null
}

Of course, for processing like this example, it would be better to use the String.toIntOrNull function.

val num = s.toIntOrNull();  // Converts to null if it cannot be converted to Int.

Additionally, when using an if expression like a ternary operator, braces {} can be omitted if there is only one expression after branching, as shown below.

println(if (x == 0) "ZERO" else "NOT ZERO")

For try ~ catch, however, {} cannot be omitted.

You cannot write it as follows.

val num = try s.toInt() catch (e: NumberFormatException) null