Custom Exceptions
In this page:
Declaring a Custom Exception
A custom exception class extends Exception (or a subclass of it), typically forwarding a message to the superclass constructor so it can be printed and inspected like any built-in exception.
Example: Declaring a Custom Exception
class InsufficientFundsException(message: String) : Exception(message)
fun withdraw(balance: Double, amount: Double): Double {
if (amount > balance) throw InsufficientFundsException("Cannot withdraw $amount from balance $balance")
return balance - amount
}
fun main() {
try {
withdraw(100.0, 150.0)
} catch (e: InsufficientFundsException) {
println("Error: ${e.message}")
}
}
Login to try C/C++/Java/PHP code in the editor
Adding Extra Properties
A custom exception can carry additional properties beyond a message, giving catching code structured information to act on programmatically instead of just parsing text.
Example: Adding Extra Properties
class ValidationException(val field: String, message: String) : Exception(message)
fun validateAge(age: Int) {
if (age < 0) throw ValidationException("age", "Age cannot be negative")
}
fun main() {
try {
validateAge(-5)
} catch (e: ValidationException) {
println("Invalid field ${e.field}: ${e.message}")
}
}
Login to try C/C++/Java/PHP code in the editor
Building a Hierarchy of Custom Exceptions
Related custom exceptions can share a common open base class, letting calling code catch either a specific exception type or the more general base type depending on how precise it needs to be.
Example: Building a Hierarchy of Custom Exceptions
open class AppException(message: String) : Exception(message)
class NetworkException(message: String) : AppException(message)
class DatabaseException(message: String) : AppException(message)
fun main() {
val errors = listOf(NetworkException("Timeout"), DatabaseException("Connection lost"))
for (error in errors) {
try {
throw error
} catch (e: AppException) {
println("App error: ${e.message}")
}
}
}
Login to try C/C++/Java/PHP code in the editor
Including a Cause
A custom exception's constructor can also accept and forward a cause (another Throwable), preserving the original underlying error while presenting a clearer, higher-level exception to callers.
Example: Including a Cause
class DataLoadException(message: String, cause: Throwable) : Exception(message, cause)
fun main() {
try {
try {
throw NumberFormatException("bad number")
} catch (e: NumberFormatException) {
throw DataLoadException("Failed to load data", e)
}
} catch (e: DataLoadException) {
println("${e.message}, caused by: ${e.cause?.message}")
}
}
Login to try C/C++/Java/PHP code in the editor
- Extending
Throwabledirectly instead ofException(orRuntimeException), which is almost never what you actually want for application-level errors. - Forgetting to pass a message (and optionally a cause) up to the superclass constructor, resulting in exceptions with no useful description when printed.
- Creating overly specific exception subclasses for every tiny situation instead of using a smaller number of well-designed, meaningful exception types.
- A custom exception is typically declared as
class MyException(message: String) : Exception(message). - Extending
Exception(a checked-style base, though Kotlin doesn't enforce checked exceptions) orRuntimeExceptionare both common choices. - Custom exceptions can carry extra properties beyond just a message, useful for programmatic error handling.
- A well-designed set of custom exceptions communicates failure reasons clearly to code that catches them.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: