Kotlin Exception Handling and Control Flow
Exception Hierarchy
In Kotlin, all exceptions inherit from the Throwable class. Each exception contains a message, stack trace, and an optional cause.
Exceptions are thrown using the throw expression:
throw RuntimeException("Something went wrong")
Try-Catch Blocks
Exception handling uses try-catch-finally blocks:
try {
performOperation()
} catch (error: SpecificException) {
handleSpecificError(error)
} catch (error: GeneralException) {
handleGeneralError(error)
} finally {
cleanupResources()
}
Zero or more catch blocks are alowed. The finally block is optional, but at least one catch or finally block must be present.
Try as an Expression
The try construct can return a value:
val result = try {
convertStringToNumber(input)
} catch (exception: NumberFormatException) {
defaultValue
} finally {
logCompletion()
}
The expression evaluates to the last expression in the try block or the last expression in any of the catch blocks. The finally block does not affect the result.
Unchecked Exceptions
Kotlin does not have checked exceptions. Consider this Java interface:
Appendable append(CharSequence sequence) throws IOException;
This would require handling IOException everywhere append is called, even when appending to a StringBuilder. Kotlin avoids this verbosity by not enforcing checked exceptions.
Nothing Type
Since throw is a expression, it can be used in contexts like the Elvis operator:
val name = user.username ?: throw IllegalStateException("Username is mandatory")
The throw expression has type Nothing, which represents unreachable code. Functions that never return should declare Nothing as their return type:
fun terminateProcess(reason: String): Nothing {
throw ProcessException(reason)
}
val username = user.name ?: terminateProcess("Missing user name")
processUsername(username) // Compiler knows username is initialized
The nullable variant Nothing? has exactly one value: null. During type inference, when a variable is initialized with null and no other type information is avialable, the compiler infers Nothing?:
val emptyValue = null // Type: Nothing?
val nullList = listOf(null) // Type: List<Nothing?>