1. Overview
In this tutorial, we’ll discuss exception handling in Kotlin.
2. Exceptions
Exceptions are problems that occur during the program execution and disrupt the conventional flow. This can occur due to various reasons like invalid arithmetic operation, a reference to a null object.
Exception handling is the technique to gracefully handle such problems and continue the program execution.
3. Kotlin Exceptions
In Kotlin, there are only unchecked exceptions that are thrown during the runtime execution of the program. All exception classes descend from the class Throwable. Kotlin uses the throw keyword to throw an exception object.
Although Kotlin inherits the concept of exception from Java, it doesn’t support checked exceptions like Java.
The checked exceptions are considered a controversial feature in Java. It decreases developer productivity without any additional increase in code quality. Among other issues, checked exceptions also lead to boilerplate code, difficulty while using lambda expressions.
So, like many other modern programming languages, the Kotlin developers also decided against including checked exceptions as a language feature.
4. Try-Catch Block
We can use the try-catch block for exception handling in Kotlin. In particular, the code that can throw an exception is put inside the try block. Additionally, the corresponding catch block is used to handle the exception.
As a matter of fact, the try block is always followed by a catch or finally block or both of them.
Let’s take a glance at a try-catch block:
try {
val message = "Welcome to Kotlin Tutorials"
message.toInt()
} catch (exception: NumberFormatException) {
// ...
}
4.1. Try-Catch Block as an Expression
An expression may be a combination of one or more values, variables, operators, and functions that execute to provide another value. Hence, we can use the try-catch block as an expression in Kotlin.
Furthermore, the return value of the try-catch expression is the last expression of either the try or the catch block. In the event of an exception, the value of the catch block is returned. However, the results of the expression are not affected by the finally block.
Here’s how we can use try-catch as an expression:
val number = try {
val message = "Welcome to Kotlin Tutorials"
message.toInt()
} catch (exception: NumberFormatException) {
// ...
}
return number
4.2. Multiple Catch Blocks
We can use multiple catch blocks together with the try block in Kotlin. Particularly, this is often required if we perform different kinds of operations in the try block, which increases the probability of catching multiple exceptions.
Besides, we must order all catch blocks from the most specific to the most general exception. As an example, the catch block for ArithmeticException must precede the catch block for Exception.
Let’s have a look at the way to use multiple catch blocks:
try {
val result = 25 / 0
result
} catch (exception: NumberFormatException) {
// ...
} catch (exception: ArithmeticException) {
// ...
} catch (exception: Exception) {
// ...
}
4.3. Nested Try-Catch Block
We can use a nested try-catch block where we implement a try-catch block inside another try block. For instance, this can be required when a block of code may throw an exception and within that block of code, another statement may additionally throw an exception.
Let’s see how we can use the nested try-catch block:
try {
val firstNumber = 50 / 2 * 0
try {
val secondNumber = 100 / firstNumber
secondNumber
} catch (exception: ArithmeticException) {
// ...
}
} catch (exception: NumberFormatException) {
// ...
}
5. Finally Block
We can use the finally block to always execute code no matter whether an exception is handled or not. Besides, we can use the finally block with the try block by omitting the catch block.
Let’s take a look into the finally block:
try {
val message = "Welcome to Kotlin Tutorials"
message.toInt()
} catch (exception: NumberFormatException) {
// ...
} finally {
// ...
}
6. Throw Keyword
We can use the throw keyword in Kotlin to throw a certain exception or a custom exception.
Here’s how we can use the throw keyword in Kotlin:
val message = "Welcome to Kotlin Tutorials"
if (message.length > 10) throw IllegalArgumentException("String is invalid")
else return message.length
We can also use throw as an expression in Kotlin. For instance, it can be used as a part of the Elvis expression:
val message: String? = null
return message?.length ?: throw IllegalArgumentException("String is null")
The throw expression returns a value of type Nothing. This special type has no values and is used to indicate an unreachable code block. In addition, we can also use the Nothing type in a function to indicate that it will always throw an exception:
fun abstractException(message: String): Nothing {
throw RuntimeException(message)
}
7. Throws Annotation
We can use the @Throws annotation to provide interoperability between Kotlin and Java. Since Kotlin doesn’t have checked exceptions, it doesn’t declare exceptions that are thrown. Let’s define a function in Kotlin:
fun readFile(): String? {
val filePath = null
return filePath ?: throw IOException("File path is invalid")
}
Now we can call the Kotlin function from Java and catch the exception. Here’s how we can write a try-catch in Java:
try {
readFile();
}
catch (IOException e) {
// ...
}
The Java compiler will display an error message because the Kotlin function didn’t declare the exception. In such cases, we can use the @Throws annotation in Kotlin to handle the error:
@Throws(IOException::class)
fun readFile(): String? {
val filePath = null
return filePath ?: throw IOException("File path is invalid")
}
8. Conclusion
In this tutorial, we discussed the various ways of exception handling in Kotlin.
As always, the code for these examples is available over on GitHub.
To learn more about Kotlin features, have a glance at one of our Kotlin tutorials.