Kotlin Exception Handling

Managing errors and unexpected situations gracefully

๐Ÿ›ก๏ธ What is Exception Handling?

Exception handling manages runtime errors gracefully, preventing crashes and providing meaningful feedback. Kotlin uses try-catch blocks to handle exceptions and maintain program stability when unexpected situations occur during execution.


// Basic exception handling
try {
    val result = 10 / 0
    println("Result: $result")
} catch (e: ArithmeticException) {
    println("Error: Cannot divide by zero!")
}
                                    

Output:

Error: Cannot divide by zero!

Exception Handling Components

๐ŸŽฏ

Try Block

Code that might throw exceptions

try { /* risky code */ }
๐Ÿ”ง

Catch Block

Handle specific exception types

catch (e: Exception) { }
๐Ÿงน

Finally Block

Code that always executes

finally { /* cleanup */ }
๐Ÿš€

Throw

Create and throw custom exceptions

throw Exception("Error")

๐Ÿ”น Try-Catch-Finally

Complete exception handling structure:

fun divideNumbers(a: Int, b: Int): String {
    return try {
        val result = a / b
        "Result: $result"
    } catch (e: ArithmeticException) {
        "Error: Division by zero is not allowed"
    } catch (e: Exception) {
        "Error: Something went wrong - ${e.message}"
    } finally {
        println("Division operation completed")
    }
}

// Test the function
println(divideNumbers(10, 2))
println(divideNumbers(10, 0))
println(divideNumbers(15, 3))

Output:

Division operation completed

Result: 5

Division operation completed

Error: Division by zero is not allowed

Division operation completed

Result: 5

๐Ÿ”น Common Exception Types

Handle different types of exceptions:

fun demonstrateExceptions() {
    // NumberFormatException
    try {
        val number = "abc".toInt()
    } catch (e: NumberFormatException) {
        println("Invalid number format: ${e.message}")
    }
    
    // IndexOutOfBoundsException
    try {
        val list = listOf(1, 2, 3)
        println(list[5])
    } catch (e: IndexOutOfBoundsException) {
        println("Index out of bounds: ${e.message}")
    }
    
    // NullPointerException
    try {
        val text: String? = null
        println(text!!.length)
    } catch (e: KotlinNullPointerException) {
        println("Null pointer exception occurred")
    }
}

demonstrateExceptions()

Output:

Invalid number format: For input string: "abc"

Index out of bounds: Index: 5, Size: 3

Null pointer exception occurred

๐Ÿ”น Custom Exceptions

Create and throw your own exception types:

// Custom exception class
class InvalidAgeException(message: String) : Exception(message)

class BankAccountException(message: String) : Exception(message)

// Functions that throw custom exceptions
fun validateAge(age: Int) {
    if (age < 0) {
        throw InvalidAgeException("Age cannot be negative: $age")
    }
    if (age > 150) {
        throw InvalidAgeException("Age seems unrealistic: $age")
    }
    println("Valid age: $age")
}

fun withdraw(balance: Double, amount: Double) {
    if (amount > balance) {
        throw BankAccountException("Insufficient funds: $balance < $amount")
    }
    println("Withdrawal successful: $amount")
}

// Using custom exceptions
try {
    validateAge(-5)
} catch (e: InvalidAgeException) {
    println("Age validation error: ${e.message}")
}

try {
    withdraw(100.0, 150.0)
} catch (e: BankAccountException) {
    println("Banking error: ${e.message}")
}

Output:

Age validation error: Age cannot be negative: -5

Banking error: Insufficient funds: 100.0 < 150.0

๐Ÿ”น Try as Expression

Use try-catch as an expression to return values:

// Try as expression
fun safeParseInt(input: String): Int {
    return try {
        input.toInt()
    } catch (e: NumberFormatException) {
        0  // Default value
    }
}

// Multiple catch blocks as expression
fun processInput(input: String): String {
    val result = try {
        val number = input.toInt()
        when {
            number > 0 -> "Positive: $number"
            number < 0 -> "Negative: $number"
            else -> "Zero"
        }
    } catch (e: NumberFormatException) {
        "Not a number: $input"
    } catch (e: Exception) {
        "Unknown error: ${e.message}"
    }
    
    return result
}

// Test the functions
println("Parse '123': ${safeParseInt("123")}")
println("Parse 'abc': ${safeParseInt("abc")}")
println("Process '42': ${processInput("42")}")
println("Process 'hello': ${processInput("hello")}")

Output:

Parse '123': 123

Parse 'abc': 0

Process '42': Positive: 42

Process 'hello': Not a number: hello

๐Ÿง  Test Your Knowledge

Which block always executes regardless of whether an exception occurs?