Errors in Kotlin

In Kotlin, it’s idiomatic to communicate errors via exceptions, which is different from the approach used in some other languages. Kotlin provides a rich set of built-in exception types, and you can also create custom exceptions.

import java.lang.Exception

// By convention, functions that can throw exceptions are marked with `@Throws`
@Throws(Exception::class)
fun f(arg: Int): Int {
    if (arg == 42) {
        // In Kotlin, we throw exceptions instead of returning them
        throw Exception("can't work with 42")
    }
    // If no exception is thrown, we simply return the result
    return arg + 3
}

// Custom exceptions are created by extending the Exception class
class OutOfTeaException : Exception("no more tea available")
class PowerException : Exception("can't boil water")

@Throws(Exception::class)
fun makeTea(arg: Int) {
    when (arg) {
        2 -> throw OutOfTeaException()
        4 -> throw PowerException()
    }
    // If no exception is thrown, the function completes normally
}

fun main() {
    for (i in listOf(7, 42)) {
        try {
            val r = f(i)
            println("f worked: $r")
        } catch (e: Exception) {
            println("f failed: ${e.message}")
        }
    }

    for (i in 0..4) {
        try {
            makeTea(i)
            println("Tea is ready!")
        } catch (e: Exception) {
            when (e) {
                is OutOfTeaException -> println("We should buy new tea!")
                is PowerException -> println("Now it is dark.")
                else -> println("unknown error: ${e.message}")
            }
        }
    }
}

In this Kotlin code:

  1. We define a function f that can throw an exception. The @Throws annotation is used to indicate that this function may throw an exception.

  2. Instead of returning errors, we throw exceptions when an error condition is met.

  3. We define custom exceptions OutOfTeaException and PowerException by extending the Exception class.

  4. The makeTea function demonstrates how to throw these custom exceptions.

  5. In the main function, we use try-catch blocks to handle potential exceptions. This is similar to error checking in other languages.

  6. We use Kotlin’s when expression (similar to switch in other languages) to check the type of the caught exception and respond accordingly.

  7. Kotlin’s smart casting allows us to use is checks in the when expression, which automatically casts the exception to the correct type in each branch.

This approach leverages Kotlin’s exception handling mechanism, which is more idiomatic for the language than returning error values. It allows for clear separation of normal and error paths in the code, while still providing detailed information about what went wrong when an error occurs.

When you run this program, you should see output similar to:

f worked: 10
f failed: can't work with 42
Tea is ready!
Tea is ready!
We should buy new tea!
Tea is ready!
Now it is dark.

This demonstrates how Kotlin handles both successful operations and various error conditions using its exception mechanism.