0
0
Kotlinprogramming~15 mins

Custom exception classes in Kotlin - Deep Dive

Choose your learning style9 modes available
Overview - Custom exception classes
What is it?
Custom exception classes in Kotlin let you create your own error types to represent specific problems in your program. Instead of using general errors, you define meaningful exceptions that explain what went wrong. This helps make your code clearer and easier to fix when errors happen. Custom exceptions are classes that extend Kotlin's built-in Exception class.
Why it matters
Without custom exceptions, all errors look the same and it becomes hard to tell what caused a problem. This slows down fixing bugs and makes your program less reliable. Custom exceptions let you catch and handle specific problems in a smart way, improving user experience and program stability. They help communicate exactly what went wrong, like a clear signpost on a road.
Where it fits
Before learning custom exceptions, you should understand basic Kotlin classes and how to handle errors with try-catch blocks. After this, you can learn about advanced error handling patterns, like sealed classes for errors or using Result types for safer code.
Mental Model
Core Idea
A custom exception class is a special kind of error you create to clearly name and handle specific problems in your program.
Think of it like...
It's like having a special alarm for your house that only rings when the kitchen smoke detector senses smoke, instead of a general alarm that rings for any problem. This way, you know exactly where the problem is.
┌─────────────────────────────┐
│        Exception Class       │
│  (built-in Kotlin class)     │
└─────────────┬───────────────┘
              │
      ┌───────┴────────┐
      │ CustomException │
      │  (your class)   │
      └─────────────────┘

Usage flow:
try {
  // code that might fail
} catch (e: CustomException) {
  // handle specific error
}
Build-Up - 7 Steps
1
FoundationUnderstanding basic exceptions
🤔
Concept: Learn what exceptions are and how Kotlin uses them to signal errors.
In Kotlin, an exception is an object that represents an error or unexpected event. When something goes wrong, Kotlin throws an exception to stop normal flow. You can catch exceptions using try-catch blocks to handle errors gracefully. Example: try { val result = 10 / 0 } catch (e: ArithmeticException) { println("Cannot divide by zero") }
Result
The program prints: Cannot divide by zero
Understanding exceptions as signals for errors is the foundation for controlling program flow when things go wrong.
2
FoundationUsing try-catch for error handling
🤔
Concept: Learn how to catch and respond to exceptions using try-catch blocks.
Try-catch blocks let you run code that might fail and catch specific exceptions to handle them. Example: try { val text = "abc" val number = text.toInt() // throws NumberFormatException } catch (e: NumberFormatException) { println("Invalid number format") }
Result
The program prints: Invalid number format
Try-catch blocks give you control to respond to errors instead of crashing the program.
3
IntermediateCreating a simple custom exception
🤔Before reading on: do you think a custom exception must have special methods or can it be empty? Commit to your answer.
Concept: You can create your own exception by making a class that extends Exception. It can be empty or have extra info.
Example: class MyCustomException(message: String) : Exception(message) fun checkAge(age: Int) { if (age < 18) { throw MyCustomException("Age must be 18 or older") } } fun main() { try { checkAge(15) } catch (e: MyCustomException) { println(e.message) } }
Result
The program prints: Age must be 18 or older
Knowing that custom exceptions can be simple classes helps you create meaningful error types without extra complexity.
4
IntermediateAdding properties to custom exceptions
🤔Before reading on: do you think custom exceptions can carry extra data besides a message? Commit to your answer.
Concept: Custom exceptions can have extra properties to hold more details about the error.
Example: class ValidationException(val field: String, message: String) : Exception(message) fun validate(name: String) { if (name.isEmpty()) { throw ValidationException("name", "Name cannot be empty") } } fun main() { try { validate("") } catch (e: ValidationException) { println("Error in field: ${e.field}, message: ${e.message}") } }
Result
The program prints: Error in field: name, message: Name cannot be empty
Adding properties to exceptions lets you pass rich error info, making handling and debugging easier.
5
IntermediateCatching multiple custom exceptions
🤔Before reading on: can you catch different custom exceptions separately in Kotlin? Commit to your answer.
Concept: You can catch different custom exceptions in separate catch blocks to handle each error type differently.
Example: class LoginException(message: String) : Exception(message) class PermissionException(message: String) : Exception(message) fun login(user: String) { if (user == "") throw LoginException("User not found") if (user == "guest") throw PermissionException("Guest has no access") } fun main() { try { login("guest") } catch (e: LoginException) { println("Login error: ${e.message}") } catch (e: PermissionException) { println("Permission error: ${e.message}") } }
Result
The program prints: Permission error: Guest has no access
Handling different exceptions separately allows precise responses to different problems.
6
AdvancedBest practices for custom exceptions
🤔Before reading on: should custom exceptions always extend Exception or can they extend Throwable directly? Commit to your answer.
Concept: Custom exceptions should extend Exception (not Throwable) and be meaningful, immutable, and documented for clarity.
Guidelines: - Extend Exception or RuntimeException, not Throwable directly. - Keep exceptions immutable (no changing properties). - Use clear names describing the error. - Include helpful messages. - Avoid using exceptions for normal control flow. Example: class DataNotFoundException(message: String) : Exception(message) fun fetchData(id: Int) { if (id < 0) throw DataNotFoundException("Invalid ID: $id") } fun main() { try { fetchData(-1) } catch (e: DataNotFoundException) { println(e.message) } }
Result
The program prints: Invalid ID: -1
Following best practices ensures your exceptions are clear, safe, and maintainable in real projects.
7
ExpertCustom exceptions and Kotlin sealed classes
🤔Before reading on: do you think sealed classes can replace custom exceptions for error handling? Commit to your answer.
Concept: Sealed classes can represent error types as a closed set, offering a safer alternative to many custom exceptions in Kotlin.
Example: sealed class AppError { data class NetworkError(val code: Int) : AppError() data class ValidationError(val field: String) : AppError() } fun handleError(error: AppError) { when (error) { is AppError.NetworkError -> println("Network error code: ${error.code}") is AppError.ValidationError -> println("Invalid field: ${error.field}") } } // This approach avoids throwing exceptions and uses explicit error values. fun main() { val error = AppError.ValidationError("email") handleError(error) }
Result
The program prints: Invalid field: email
Understanding sealed classes as an alternative to exceptions helps write safer, more predictable error handling code.
Under the Hood
When Kotlin code throws an exception, the runtime creates an instance of the exception class and unwinds the call stack until it finds a matching catch block. Custom exceptions are normal classes inheriting from Exception, so they behave like any object but signal errors. The stack trace is captured to show where the error happened. The JVM uses this to manage program flow during errors.
Why designed this way?
Kotlin inherits exception handling from Java, which uses class inheritance to represent errors. This design allows flexible error types and polymorphic catching. Custom exceptions let developers add meaning to errors without changing the runtime. Alternatives like error codes were less expressive and harder to manage.
┌───────────────┐
│  throw Error │
└───────┬───────┘
        │
        ▼
┌─────────────────────┐
│ Exception instance   │
│ (custom or built-in) │
└─────────┬───────────┘
          │
          ▼
┌───────────────────────────────┐
│ Stack unwinds to catch block   │
│ matching exception type        │
└─────────────┬─────────────────┘
              │
      ┌───────┴────────┐
      │ Exception handled │
      └──────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do you think custom exceptions must always have extra methods beyond a message? Commit to yes or no.
Common Belief:Custom exceptions need special methods or complex code to be useful.
Tap to reveal reality
Reality:Custom exceptions can be simple classes that just carry a message or extra data; no special methods are required.
Why it matters:Believing this makes learners avoid creating custom exceptions, missing out on clearer error handling.
Quick: Do you think catching Exception catches all errors including system errors? Commit to yes or no.
Common Belief:Catching Exception catches every possible error in Kotlin.
Tap to reveal reality
Reality:Some errors like OutOfMemoryError inherit from Throwable but not Exception, so they are not caught by catching Exception.
Why it matters:Assuming all errors are caught can lead to missed critical failures and unstable programs.
Quick: Do you think using exceptions for normal program flow is a good practice? Commit to yes or no.
Common Belief:Throwing exceptions is fine for regular control flow like checking conditions.
Tap to reveal reality
Reality:Exceptions should represent unexpected errors, not normal conditions; using them for flow hurts performance and clarity.
Why it matters:Misusing exceptions can slow programs and make code harder to understand and maintain.
Quick: Do you think sealed classes and custom exceptions serve the same purpose? Commit to yes or no.
Common Belief:Sealed classes and custom exceptions are interchangeable for error handling.
Tap to reveal reality
Reality:Sealed classes represent error states as values without throwing, while exceptions signal errors by interrupting flow; they serve related but different roles.
Why it matters:Confusing these leads to mixing error handling styles and less predictable code.
Expert Zone
1
Custom exceptions should be immutable to avoid confusing error states during handling.
2
Using meaningful exception names improves debugging and logging clarity in large codebases.
3
Stack traces from custom exceptions help pinpoint error origins but can be expensive to create; avoid throwing exceptions in performance-critical loops.
When NOT to use
Avoid custom exceptions when simple error codes or sealed classes can represent errors more safely and clearly. For predictable error handling, prefer sealed classes or Result types to exceptions which are better for unexpected failures.
Production Patterns
In production, custom exceptions are used to signal domain-specific errors like validation failures or resource not found. They are often logged with context and caught at boundaries to show user-friendly messages or retry logic. Combining custom exceptions with sealed classes or Result wrappers is common for robust error handling.
Connections
Sealed classes in Kotlin
Alternative error representation
Knowing sealed classes helps understand safer, explicit error handling without exceptions, improving program predictability.
Error handling in functional programming
Different approach to errors
Functional programming often avoids exceptions, using types like Either or Result to represent errors, which contrasts with exception throwing but shares the goal of clear error management.
Medical diagnosis process
Categorizing and signaling problems
Just like doctors classify symptoms into specific diagnoses to treat patients effectively, custom exceptions classify errors to handle them precisely in programs.
Common Pitfalls
#1Throwing generic exceptions without clear meaning
Wrong approach:throw Exception("Something went wrong")
Correct approach:class DataMissingException(message: String) : Exception(message) throw DataMissingException("User data missing")
Root cause:Not creating specific exceptions leads to vague error messages and harder debugging.
#2Catching all exceptions and ignoring them
Wrong approach:try { riskyOperation() } catch (e: Exception) { // do nothing }
Correct approach:try { riskyOperation() } catch (e: SpecificException) { handleError(e) }
Root cause:Catching all exceptions without handling hides errors and makes bugs hard to find.
#3Using exceptions for normal control flow
Wrong approach:fun isValid(input: String): Boolean { try { checkInput(input) return true } catch (e: Exception) { return false } }
Correct approach:fun isValid(input: String): Boolean { return input.isNotEmpty() && input.all { it.isLetter() } }
Root cause:Misunderstanding exceptions as normal checks causes performance issues and unclear code.
Key Takeaways
Custom exception classes let you create clear, meaningful error types that improve program clarity and debugging.
They are simple Kotlin classes extending Exception and can carry extra information about the error.
Using specific exceptions helps catch and handle different problems precisely, avoiding vague error handling.
Best practice is to keep exceptions immutable, well-named, and avoid using them for normal program flow.
Alternatives like sealed classes offer safer error handling patterns that complement or replace exceptions in Kotlin.