Kotlin Exception Handling
An exception is an event that disrupts the normal flow of a program — like trying to divide by zero, reading a file that does not exist, or parsing text that is not a valid number. Exception handling lets your program detect these problems, respond gracefully, and continue running rather than crashing.
try / catch / finally
Wrap risky code in a try block. If an exception occurs, execution jumps to the matching catch block. The finally block always runs, regardless of whether an exception happened.
try {
val result = 10 / 0
println(result)
} catch (e: ArithmeticException) {
println("Cannot divide by zero: ${e.message}")
} finally {
println("This always runs")
}
// Output:
// Cannot divide by zero: / by zero
// This always runs
Diagram — try/catch/finally Flow
try {
risky code ──────► No exception ─────────────────────────┐
│ │
▼ Exception thrown │
catch (e) { │
handle error ──────────────────────────────────────────► │
} │
▼
finally { always runs here
cleanup
}
Multiple catch Blocks
fun parseInput(input: String) {
try {
val number = input.toInt()
println("Parsed: ${100 / number}")
} catch (e: NumberFormatException) {
println("Not a number: $input")
} catch (e: ArithmeticException) {
println("Cannot divide by zero")
} catch (e: Exception) {
println("Unexpected error: ${e.message}")
}
}
parseInput("abc") // Not a number: abc
parseInput("0") // Cannot divide by zero
parseInput("5") // Parsed: 20
try as an Expression
In Kotlin, try is an expression — it returns a value. The last expression in try or catch becomes the result.
val value: Int = try {
"42".toInt()
} catch (e: NumberFormatException) {
0
}
println(value) // 42
val broken: Int = try {
"abc".toInt()
} catch (e: NumberFormatException) {
-1
}
println(broken) // -1
Throwing Exceptions
Use the throw keyword to signal that something went wrong. You can throw any class that extends Throwable.
fun setAge(age: Int) {
if (age < 0 || age > 150) {
throw IllegalArgumentException("Age must be between 0 and 150, got $age")
}
println("Age set to $age")
}
try {
setAge(-5)
} catch (e: IllegalArgumentException) {
println("Error: ${e.message}")
}
// Error: Age must be between 0 and 150, got -5
Common Built-In Exception Types
Exception Type | When it occurs ----------------------------|----------------------------------------------- NullPointerException | Accessing a member on a null reference ArrayIndexOutOfBounds | Accessing index beyond array size NumberFormatException | Parsing "abc" as a number IllegalArgumentException | A parameter value is not acceptable IllegalStateException | Object is in wrong state for the operation ArithmeticException | Math error (divide by zero) ClassCastException | Casting to an incompatible type UnsupportedOperationException| Operation not supported by the implementation
Creating Custom Exceptions
class InsufficientFundsException(amount: Double)
: Exception("Not enough balance. Needed: ₹$amount")
class BankAccount(var balance: Double) {
fun withdraw(amount: Double) {
if (amount > balance) throw InsufficientFundsException(amount)
balance -= amount
println("Withdrew ₹$amount. Balance: ₹$balance")
}
}
val account = BankAccount(500.0)
try {
account.withdraw(300.0) // Withdrew ₹300.0. Balance: ₹200.0
account.withdraw(400.0) // throws
} catch (e: InsufficientFundsException) {
println(e.message) // Not enough balance. Needed: ₹400.0
}
require / check / error — Preconditions
Kotlin provides three functions for common validation patterns that throw exceptions automatically.
// require — validates function arguments
fun setPercentage(value: Int) {
require(value in 0..100) { "Percentage must be 0–100, got $value" }
println("Set to $value%")
}
setPercentage(110) // IllegalArgumentException: Percentage must be 0–100...
// check — validates object state
var isInitialized = false
fun start() {
check(isInitialized) { "Must call initialize() first" }
println("Starting...")
}
start() // IllegalStateException: Must call initialize() first
// error — unconditionally throws IllegalStateException
fun getUser(role: String): String {
return when (role) {
"admin" -> "Admin User"
"guest" -> "Guest User"
else -> error("Unknown role: $role")
}
}
getUser("hacker") // IllegalStateException: Unknown role: hacker
runCatching — Functional Exception Handling
runCatching wraps a block in a Result object. Use it when you want to handle success and failure as values rather than with try/catch blocks.
val result = runCatching {
"not a number".toInt()
}
result
.onSuccess { println("Parsed: $it") }
.onFailure { println("Failed: ${it.message}") }
// Failed: For input string: "not a number"
val value = runCatching { "42".toInt() }.getOrDefault(0)
println(value) // 42
