Skip to main content

Kotlin Exceptions

Exception handling is a crucial aspect of writing robust applications. In this lesson, we'll explore how Kotlin handles errors through its exception mechanism, which allows your program to respond gracefully when something unexpected happens during execution.

What Are Exceptions?

Exceptions are special events that disrupt the normal flow of a program when an error occurs. Instead of allowing your program to crash, exceptions provide a way to detect problems at runtime and handle them appropriately.

In Kotlin, exceptions are represented by instances of classes that inherit from the Throwable class. The two main subclasses are:

  • Error: Represents serious problems that a reasonable application shouldn't try to catch
  • Exception: Represents conditions that a reasonable application might want to catch

Throwing Exceptions

You can throw an exception in Kotlin using the throw keyword:

kotlin
fun validateAge(age: Int) {
if (age < 0) {
throw IllegalArgumentException("Age cannot be negative")
}
println("Age is valid: $age")
}

// Example usage:
fun main() {
try {
validateAge(25) // Works fine
validateAge(-5) // This will throw an exception
} catch (e: IllegalArgumentException) {
println("Caught an exception: ${e.message}")
}
}

Output:

Age is valid: 25
Caught an exception: Age cannot be negative

Basic Exception Handling with try-catch

The primary mechanism for exception handling in Kotlin is the try-catch block:

kotlin
fun main() {
try {
// Code that might throw an exception
val result = 10 / 0
println("This line will never execute")
} catch (e: ArithmeticException) {
// Code to handle the exception
println("Cannot divide by zero: ${e.message}")
}

// Program continues execution
println("Program continues running")
}

Output:

Cannot divide by zero: / by zero
Program continues running

Multiple catch Blocks

You can handle different types of exceptions with multiple catch blocks:

kotlin
fun main() {
try {
val numbers = listOf(1, 2, 3)
println(numbers[5]) // This will throw IndexOutOfBoundsException

val result = 10 / 0 // This would throw ArithmeticException but won't be reached
} catch (e: IndexOutOfBoundsException) {
println("Index error: ${e.message}")
} catch (e: ArithmeticException) {
println("Arithmetic error: ${e.message}")
} catch (e: Exception) {
// Catch any other exceptions that weren't caught above
println("Some other exception: ${e.message}")
}
}

Output:

Index error: Index 5 out of bounds for length 3

The finally Block

The finally block always executes regardless of whether an exception was thrown or caught:

kotlin
fun readFile(filename: String) {
var file: java.io.FileReader? = null

try {
file = java.io.FileReader(filename)
// Process file content
println("Reading file: $filename")

// This will throw an exception if the file doesn't exist
} catch (e: java.io.FileNotFoundException) {
println("File not found: ${e.message}")
} finally {
// This code always runs, whether exception occurs or not
println("Closing file resources")
file?.close()
}
}

fun main() {
readFile("existing-file.txt") // Assume this file exists
readFile("non-existent-file.txt") // This file doesn't exist
}

Output (if first file exists):

Reading file: existing-file.txt
Closing file resources
File not found: non-existent-file.txt (No such file or directory)
Closing file resources

try-catch as an Expression

In Kotlin, try-catch can be used as an expression that returns a value:

kotlin
fun parseInteger(str: String): Int {
val result = try {
str.toInt()
} catch (e: NumberFormatException) {
0 // Default value when parsing fails
}

return result
}

fun main() {
println(parseInteger("42")) // Success case
println(parseInteger("4.2")) // Failure case
println(parseInteger("hello")) // Failure case
}

Output:

42
0
0

Creating Custom Exceptions

You can create your own exception types by extending the Exception class or any of its subclasses:

kotlin
class InsufficientFundsException(message: String) : Exception(message)

class BankAccount(private var balance: Double) {
fun withdraw(amount: Double) {
if (amount > balance) {
throw InsufficientFundsException("Cannot withdraw $amount, balance is $balance")
}
balance -= amount
println("Withdrew $amount. New balance: $balance")
}
}

fun main() {
val account = BankAccount(100.0)

try {
account.withdraw(50.0) // Works fine
account.withdraw(75.0) // Throws InsufficientFundsException
} catch (e: InsufficientFundsException) {
println("Transaction failed: ${e.message}")
}
}

Output:

Withdrew 50.0. New balance: 50.0
Transaction failed: Cannot withdraw 75.0, balance is 50.0

Checked vs. Unchecked Exceptions in Kotlin

Unlike Java, Kotlin doesn't have checked exceptions. All exceptions in Kotlin are unchecked, meaning you're not forced to catch or declare any exceptions. This design decision was made to avoid boilerplate code.

try-with-resources in Kotlin (use function)

Kotlin doesn't have a built-in try-with-resources statement like Java, but instead provides the use extension function to automatically close resources:

kotlin
import java.io.BufferedReader
import java.io.FileReader

fun readFirstLine(path: String): String {
BufferedReader(FileReader(path)).use { reader ->
return reader.readLine() ?: "File was empty"
}
// The reader is automatically closed when the block exits
}

fun main() {
try {
val firstLine = readFirstLine("example.txt")
println("First line: $firstLine")
} catch (e: java.io.IOException) {
println("Error reading file: ${e.message}")
}
}

When to Use Exceptions

Exceptions should be used for exceptional conditions, not for normal flow control:

  • Good use case: A file you're trying to read doesn't exist
  • Bad use case: Using exceptions to determine if an element is in a list (use normal control flow instead)

Real-World Example: Form Validation

Here's a real-world example of using exceptions for validating user input in a form:

kotlin
// Custom exceptions
class ValidationException(message: String) : Exception(message)
class EmptyFieldException(fieldName: String) : ValidationException("The $fieldName field cannot be empty")
class InvalidEmailException(email: String) : ValidationException("'$email' is not a valid email address")
class PasswordTooWeakException : ValidationException("Password must contain at least 8 characters, including a number and a special character")

class UserRegistrationForm(
val username: String,
val email: String,
val password: String
) {
fun validate() {
validateUsername()
validateEmail()
validatePassword()
println("All fields are valid!")
}

private fun validateUsername() {
if (username.isBlank()) {
throw EmptyFieldException("username")
}
}

private fun validateEmail() {
if (email.isBlank()) {
throw EmptyFieldException("email")
}

// Simple email validation
if (!email.contains("@") || !email.contains(".")) {
throw InvalidEmailException(email)
}
}

private fun validatePassword() {
if (password.length < 8 || !password.any { it.isDigit() } || !password.any { !it.isLetterOrDigit() }) {
throw PasswordTooWeakException()
}
}
}

fun main() {
val forms = listOf(
UserRegistrationForm("john_doe", "[email protected]", "p@ssw0rd"),
UserRegistrationForm("", "[email protected]", "password123!"),
UserRegistrationForm("jane_doe", "not-an-email", "strongpass1!"),
UserRegistrationForm("bob_smith", "[email protected]", "weak")
)

for ((index, form) in forms.withIndex()) {
println("\nValidating form #${index + 1}...")
try {
form.validate()
println("Registration successful for user: ${form.username}")
} catch (e: ValidationException) {
println("Registration failed: ${e.message}")
}
}
}

Output:

Validating form #1...
All fields are valid!
Registration successful for user: john_doe

Validating form #2...
Registration failed: The username field cannot be empty

Validating form #3...
Registration failed: 'not-an-email' is not a valid email address

Validating form #4...
Registration failed: Password must contain at least 8 characters, including a number and a special character

Summary

In this lesson, you've learned:

  • How to throw and catch exceptions in Kotlin
  • How to use try-catch blocks for error handling
  • The importance of the finally block for resource cleanup
  • How to create custom exception classes
  • Using try-catch as an expression
  • The use function for resource management
  • Best practices for exception handling in real-world applications

Exception handling is essential for creating resilient applications that can gracefully handle unexpected errors. By mastering exceptions in Kotlin, you'll be able to write code that's more robust and user-friendly.

Exercises

  1. Create a calculator program that handles various exceptions like division by zero and invalid operations.
  2. Write a function that reads a file and handles common IO exceptions.
  3. Create a custom exception hierarchy for a banking application that includes exceptions for insufficient funds, account not found, and daily withdrawal limit exceeded.
  4. Extend the form validation example to include additional validations like phone number format and age restrictions.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)