Android Error Handling Kotlin Samples

Android Kotlin error handling examples including exception catching, logging, and parameter validation

💻 Logging kotlin

🟢 simple ⭐⭐⭐

Write logs to files and console with various levels and formatting

⏱️ 20 min 🏷️ kotlin, android, logging
Prerequisites: Basic Kotlin, Android SDK
// Android Kotlin Logging Examples
// Using android.util.Log and file-based logging

import android.util.Log
import android.content.Context
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

// 1. Basic Android Logging
class BasicLogger {

    companion object {
        private const val TAG = "BasicLogger"
    }

    // Debug log
    fun logDebug(message: String) {
        Log.d(TAG, message)
    }

    // Info log
    fun logInfo(message: String) {
        Log.i(TAG, message)
    }

    // Warning log
    fun logWarning(message: String) {
        Log.w(TAG, message)
    }

    // Error log
    fun logError(message: String) {
        Log.e(TAG, message)
    }

    // Error log with exception
    fun logError(message: String, throwable: Throwable) {
        Log.e(TAG, message, throwable)
    }

    // Verbose log
    fun logVerbose(message: String) {
        Log.v(TAG, message)
    }

    // What a terrible failure log
    fun logWtf(message: String) {
        Log.wtf(TAG, message)
    }
}

// 2. File-based Logging
class FileLogger(private val context: Context) {

    private val logFile = File(context.filesDir, "app.log")

    // Write log to file
    fun writeLog(level: String, tag: String, message: String) {
        try {
            val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
                .format(Date())

            val logEntry = "[$timestamp] [$level] [$tag] $message\n"

            FileWriter(logFile, true).use { writer ->
                writer.write(logEntry)
            }
        } catch (e: Exception) {
            Log.e("FileLogger", "Failed to write log", e)
        }
    }

    // Debug log to file
    fun d(tag: String, message: String) {
        writeLog("DEBUG", tag, message)
        Log.d(tag, message)
    }

    // Info log to file
    fun i(tag: String, message: String) {
        writeLog("INFO", tag, message)
        Log.i(tag, message)
    }

    // Warning log to file
    fun w(tag: String, message: String) {
        writeLog("WARN", tag, message)
        Log.w(tag, message)
    }

    // Error log to file
    fun e(tag: String, message: String) {
        writeLog("ERROR", tag, message)
        Log.e(tag, message)
    }

    // Error log with throwable
    fun e(tag: String, message: String, throwable: Throwable) {
        writeLog("ERROR", tag, "$message: ${throwable.message}")
        Log.e(tag, message, throwable)
    }

    // Read log file
    fun readLogs(): String {
        return try {
            if (logFile.exists()) {
                logFile.readText()
            } else {
                "No log file found"
            }
        } catch (e: Exception) {
            "Error reading logs: ${e.message}"
        }
    }

    // Clear log file
    fun clearLogs() {
        try {
            if (logFile.exists()) {
                logFile.delete()
                Log.i("FileLogger", "Log file cleared")
            }
        } catch (e: Exception) {
            Log.e("FileLogger", "Failed to clear logs", e)
        }
    }

    // Get log file size
    fun getLogFileSize(): Long {
        return if (logFile.exists()) {
            logFile.length()
        } else {
            0L
        }
    }
}

// 3. Structured Logging
class StructuredLogger(private val context: Context) {

    private val fileLogger = FileLogger(context)
    private val tag = "StructuredLogger"

    // Log structured data
    fun logInfo(level: String, message: String, metadata: Map<String, Any?>) {
        val metadataString = metadata.map { (key, value) ->
            "$key=$value"
        }.joinToString(", ")

        val fullMessage = "$message [$metadataString]"

        when (level.uppercase()) {
            "DEBUG" -> fileLogger.d(tag, fullMessage)
            "INFO" -> fileLogger.i(tag, fullMessage)
            "WARN" -> fileLogger.w(tag, fullMessage)
            "ERROR" -> fileLogger.e(tag, fullMessage)
        }
    }

    // Log user action
    fun logUserAction(action: String, userId: String?, details: Map<String, Any?>) {
        logInfo("INFO", "User action: $action", mapOf(
            "userId" to userId,
            "timestamp" to System.currentTimeMillis(),
            "details" to details.toString()
        ))
    }

    // Log API call
    fun logApiCall(endpoint: String, method: String, statusCode: Int?, responseTime: Long) {
        logInfo("INFO", "API call", mapOf(
            "endpoint" to endpoint,
            "method" to method,
            "statusCode" to statusCode,
            "responseTime" to responseTime
        ))
    }

    // Log error with context
    fun logError(error: Throwable, context: Map<String, Any?>) {
        val sw = StringWriter()
        error.printStackTrace(PrintWriter(sw))

        fileLogger.e(tag, "Error: ${error.message}\nContext: $context\nStack: ${sw.toString()}")
    }
}

// 4. Logger with Levels
class LevelLogger(private val context: Context) {

    enum class LogLevel { VERBOSE, DEBUG, INFO, WARN, ERROR }

    private var currentLevel = LogLevel.INFO

    fun setLevel(level: LogLevel) {
        currentLevel = level
    }

    // Log at specified level
    fun log(level: LogLevel, tag: String, message: String) {
        if (level.ordinal >= currentLevel.ordinal) {
            when (level) {
                LogLevel.VERBOSE -> Log.v(tag, message)
                LogLevel.DEBUG -> Log.d(tag, message)
                LogLevel.INFO -> Log.i(tag, message)
                LogLevel.WARN -> Log.w(tag, message)
                LogLevel.ERROR -> Log.e(tag, message)
            }
        }
    }

    // Convenience methods
    fun v(tag: String, message: String) = log(LogLevel.VERBOSE, tag, message)
    fun d(tag: String, message: String) = log(LogLevel.DEBUG, tag, message)
    fun i(tag: String, message: String) = log(LogLevel.INFO, tag, message)
    fun w(tag: String, message: String) = log(LogLevel.WARN, tag, message)
    fun e(tag: String, message: String) = log(LogLevel.ERROR, tag, message)
}

// 5. Crash Reporting Logger
class CrashReportingLogger(private val context: Context) {

    private val crashFile = File(context.filesDir, "crash.log")

    // Capture uncaught exception
    fun captureException(thread: Thread, throwable: Throwable) {
        try {
            val timestamp = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
                .format(Date())

            val report = StringBuilder()
            report.appendLine("=== CRASH REPORT ===")
            report.appendLine("Timestamp: $timestamp")
            report.appendLine("Thread: ${thread.name}")
            report.appendLine("Exception: ${throwable.javaClass.simpleName}")
            report.appendLine("Message: ${throwable.message}")
            report.appendLine("\nStack Trace:")
            report.appendLine(getStackTrace(throwable))

            // Log to file
            FileWriter(crashFile, true).use { writer ->
                writer.write(report.toString())
                writer.write("\n\n")
            }

            Log.e("CrashReporter", "Crash captured", throwable)
        } catch (e: Exception) {
            Log.e("CrashReporter", "Failed to capture crash", e)
        }
    }

    // Get stack trace as string
    private fun getStackTrace(throwable: Throwable): String {
        val sw = StringWriter()
        val pw = PrintWriter(sw)
        throwable.printStackTrace(pw)
        return sw.toString()
    }

    // Read crash reports
    fun readCrashReports(): String {
        return try {
            if (crashFile.exists()) {
                crashFile.readText()
            } else {
                "No crash reports found"
            }
        } catch (e: Exception) {
            "Error reading crash reports: ${e.message}"
        }
    }

    // Clear crash reports
    fun clearCrashReports() {
        try {
            if (crashFile.exists()) {
                crashFile.delete()
            }
        } catch (e: Exception) {
            Log.e("CrashReporter", "Failed to clear crash reports", e)
        }
    }
}

// 6. Performance Logging
class PerformanceLogger(private val context: Context) {

    private val fileLogger = FileLogger(context)
    private val tag = "PerformanceLogger"

    // Measure execution time
    fun <T> measureTime(tag: String, operation: String, block: () -> T): T {
        val startTime = System.nanoTime()

        return try {
            val result = block()

            val endTime = System.nanoTime()
            val durationMs = (endTime - startTime) / 1_000_000.0

            fileLogger.i(tag, "$operation completed in ${String.format("%.2f", durationMs)}ms")

            result
        } catch (e: Exception) {
            val endTime = System.nanoTime()
            val durationMs = (endTime - startTime) / 1_000_000.0

            fileLogger.e(tag, "$operation failed after ${String.format("%.2f", durationMs)}ms: ${e.message}")

            throw e
        }
    }

    // Log memory usage
    fun logMemoryUsage(tag: String, context: String) {
        val runtime = Runtime.getRuntime()
        val usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024
        val maxMemory = runtime.maxMemory() / 1024 / 1024
        val totalMemory = runtime.totalMemory() / 1024 / 1024

        fileLogger.i(tag, "Memory [$context]: Used=${usedMemory}MB, Total=${totalMemory}MB, Max=${maxMemory}MB")
    }

    // Start a timer
    fun startTimer(operation: String): Timer {
        return Timer(operation)
    }

    // Timer class
    inner class Timer(private val operation: String) {
        private val startTime = System.nanoTime()

        fun end() {
            val endTime = System.nanoTime()
            val durationMs = (endTime - startTime) / 1_000_000.0

            fileLogger.i(tag, "$operation took ${String.format("%.2f", durationMs)}ms")
        }
    }
}

// 7. Remote Logging (Simplified)
class RemoteLogger(private val context: Context) {

    private val fileLogger = FileLogger(context)

    // Queue logs for remote upload
    private val logQueue = mutableListOf<String>()

    // Add log to queue
    fun queueLog(level: String, tag: String, message: String) {
        val logEntry = mapOf(
            "timestamp" to System.currentTimeMillis(),
            "level" to level,
            "tag" to tag,
            "message" to message
        )

        synchronized(logQueue) {
            logQueue.add(logEntry.toString())
        }

        // Keep queue size manageable
        if (logQueue.size > 1000) {
            logQueue.removeAt(0)
        }
    }

    // Upload logs (simulated)
    fun uploadLogs(): Boolean {
        return try {
            synchronized(logQueue) {
                if (logQueue.isNotEmpty()) {
                    println("Uploading ${logQueue.size} log entries...")
                    // In real app, send to remote server
                    logQueue.clear()
                    true
                } else {
                    false
                }
            }
        } catch (e: Exception) {
            fileLogger.e("RemoteLogger", "Upload failed: ${e.message}")
            false
        }
    }

    // Get queue size
    fun getQueueSize(): Int {
        return synchronized(logQueue) {
            logQueue.size
        }
    }
}

// Main demonstration
fun demonstrateLogging(context: Context) {
    println("=== Android Kotlin Logging Examples ===\n")

    // 1. Basic logging
    println("--- 1. Basic Android Logging ---")
    val basicLogger = BasicLogger()

    basicLogger.logVerbose("Verbose message")
    basicLogger.logDebug("Debug message")
    basicLogger.logInfo("Info message")
    basicLogger.logWarning("Warning message")
    basicLogger.logError("Error message")

    try {
        throw RuntimeException("Test exception")
    } catch (e: Exception) {
        basicLogger.logError("Caught exception", e)
    }

    // 2. File logging
    println("\n--- 2. File Logging ---")
    val fileLogger = FileLogger(context)

    fileLogger.d("FileLog", "Debug log to file")
    fileLogger.i("FileLog", "Info log to file")
    fileLogger.w("FileLog", "Warning log to file")
    fileLogger.e("FileLog", "Error log to file")

    println("Log file size: ${fileLogger.getLogFileSize()} bytes")

    // 3. Structured logging
    println("\n--- 3. Structured Logging ---")
    val structuredLogger = StructuredLogger(context)

    structuredLogger.logUserAction("login", "user123", mapOf(
        "ip" to "192.168.1.1",
        "userAgent" to "Mozilla/5.0"
    ))

    structuredLogger.logApiCall("/api/users", "GET", 200, 125)

    try {
        throw RuntimeException("API error")
    } catch (e: Exception) {
        structuredLogger.logError(e, mapOf("endpoint" to "/api/users"))
    }

    // 4. Level logging
    println("\n--- 4. Level Logging ---")
    val levelLogger = LevelLogger(context)

    levelLogger.setLevel(LevelLogger.LogLevel.WARN)

    levelLogger.v("LevelLog", "This won't be logged")
    levelLogger.d("LevelLog", "This won't be logged")
    levelLogger.i("LevelLog", "This won't be logged")
    levelLogger.w("LevelLog", "This will be logged")
    levelLogger.e("LevelLog", "This will be logged")

    // 5. Performance logging
    println("\n--- 5. Performance Logging ---")
    val perfLogger = PerformanceLogger(context)

    perfLogger.logMemoryUsage("PerfLog", "Before operation")

    val result = perfLogger.measureTime("PerfLog", "Test operation") {
        Thread.sleep(100)
        "Operation result"
    }

    println("Result: $result")

    perfLogger.logMemoryUsage("PerfLog", "After operation")

    // Using timer
    val timer = perfLogger.startTimer("Manual timer")
    Thread.sleep(50)
    timer.end()

    // 6. Remote logging
    println("\n--- 6. Remote Logging ---")
    val remoteLogger = RemoteLogger(context)

    remoteLogger.queueLog("INFO", "RemoteLog", "Log entry 1")
    remoteLogger.queueLog("ERROR", "RemoteLog", "Log entry 2")
    remoteLogger.queueLog("WARN", "RemoteLog", "Log entry 3")

    println("Queue size: ${remoteLogger.getQueueSize()}")

    remoteLogger.uploadLogs()
    println("Queue size after upload: ${remoteLogger.getQueueSize()}")

    println("\n=== All Logging Examples Completed ===")
}

💻 Parameter Validation kotlin

🟢 simple ⭐⭐

Validate function parameters using require, check, and custom validators

⏱️ 15 min 🏷️ kotlin, android, validation
Prerequisites: Basic Kotlin
// Android Kotlin Parameter Validation Examples
// Using Kotlin's built-in validation features

// 1. Basic Validation with require()
class BasicValidation {

    // Validate positive number
    fun calculateDiscount(price: Double, discount: Double): Double {
        require(price > 0) { "Price must be positive, was: $price" }
        require(discount in 0.0..100.0) { "Discount must be between 0 and 100, was: $discount" }

        return price * (1 - discount / 100)
    }

    // Validate string parameters
    fun createUsername(firstName: String, lastName: String): String {
        require(firstName.isNotBlank()) { "First name cannot be blank" }
        require(lastName.isNotBlank()) { "Last name cannot be blank" }

        return "${firstName.lowercase()}_${lastName.lowercase()}"
    }

    // Validate collection
    fun getFirstElement(list: List<String>): String {
        require(list.isNotEmpty()) { "List cannot be empty" }

        return list.first()
    }

    // Validate range
    fun setVolume(volume: Int): Int {
        require(volume in 0..100) { "Volume must be between 0 and 100, was: $volume" }

        return volume
    }

    // Validate with multiple conditions
    fun processUser(name: String, age: Int, email: String): Boolean {
        require(name.isNotBlank()) { "Name cannot be blank" }
        require(age in 18..120) { "Age must be between 18 and 120, was: $age" }
        require(email.contains("@")) { "Email must contain '@'" }

        println("User validated: $name, $age, $email")
        return true
    }
}

// 2. State Validation with check()
class StateValidation {

    private var initialized = false
    private var connected = false

    // Initialize before use
    fun initialize() {
        initialized = true
        println("Initialized")
    }

    // Connect to server
    fun connect() {
        check(initialized) { "Must initialize before connecting" }

        connected = true
        println("Connected")
    }

    // Send data
    fun sendData(data: String) {
        check(connected) { "Must be connected to send data" }

        println("Data sent: $data")
    }

    // Check state before operation
    fun performOperation() {
        check(initialized) { "Not initialized" }
        check(connected) { "Not connected" }

        println("Operation performed")
    }
}

// 3. Custom Validation Functions
class CustomValidators {

    // Validate email format
    fun isValidEmail(email: String): Boolean {
        val emailRegex = Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")
        return emailRegex.matches(email)
    }

    // Validate and return or throw
    fun validateEmail(email: String): String {
        require(isValidEmail(email)) { "Invalid email format: $email" }
        return email
    }

    // Validate phone number
    fun isValidPhoneNumber(phone: String): Boolean {
        val phoneRegex = Regex("^\\+?[1-9]\\d{1,14}$")
        return phoneRegex.matches(phone)
    }

    // Validate URL
    fun isValidUrl(url: String): Boolean {
        try {
            java.net.URL(url).toURI()
            return true
        } catch (e: Exception) {
            return false
        }
    }

    // Validate password strength
    enum class PasswordStrength { WEAK, MEDIUM, STRONG }

    fun checkPasswordStrength(password: String): PasswordStrength {
        if (password.length < 8) {
            return PasswordStrength.WEAK
        }

        val hasUpperCase = password.any { it.isUpperCase() }
        val hasLowerCase = password.any { it.isLowerCase() }
        val hasDigit = password.any { it.isDigit() }
        val hasSpecial = password.any { !it.isLetterOrDigit() }

        return when {
            hasUpperCase && hasLowerCase && hasDigit && hasSpecial -> PasswordStrength.STRONG
            hasUpperCase && hasLowerCase && hasDigit -> PasswordStrength.MEDIUM
            else -> PasswordStrength.WEAK
        }
    }

    // Validate password with minimum strength
    fun validatePassword(password: String, minStrength: PasswordStrength = PasswordStrength.MEDIUM): String {
        val strength = checkPasswordStrength(password)

        require(strength.ordinal >= minStrength.ordinal) {
            "Password strength must be at least $minStrength, was: $strength"
        }

        return password
    }

    // Validate date range
    fun isValidDateRange(start: java.util.Date, end: java.util.Date): Boolean {
        return start.before(end) || start.equals(end)
    }

    // Validate and sanitize string
    fun sanitizeAndValidate(input: String, maxLength: Int = 100): String {
        val sanitized = input.trim()

        require(sanitized.isNotEmpty()) { "Input cannot be empty" }
        require(sanitized.length <= maxLength) { "Input exceeds maximum length of $maxLength" }

        return sanitized
    }
}

// 4. Validation with Error Collection
class ValidationCollector {

    // Collect multiple validation errors
    fun validateUser(data: Map<String, Any?>): ValidationResult {
        val errors = mutableListOf<String>()

        val name = data["name"] as? String
        if (name.isNullOrBlank()) {
            errors.add("Name is required")
        }

        val age = data["age"] as? Int
        if (age == null) {
            errors.add("Age is required")
        } else if (age !in 18..120) {
            errors.add("Age must be between 18 and 120")
        }

        val email = data["email"] as? String
        if (email.isNullOrBlank()) {
            errors.add("Email is required")
        } else if (!email.contains("@")) {
            errors.add("Email must contain '@'")
        }

        return ValidationResult(
            isValid = errors.isEmpty(),
            errors = errors
        )
    }

    // Validation result data class
    data class ValidationResult(
        val isValid: Boolean,
        val errors: List<String>
    ) {
        fun getErrorString(): String {
            return errors.joinToString("; ")
        }
    }

    // Validate with early return
    fun validateUserEarlyReturn(data: Map<String, Any?>): ValidationResult {
        val name = data["name"] as? String
        if (name.isNullOrBlank()) {
            return ValidationResult(false, listOf("Name is required"))
        }

        val age = data["age"] as? Int
        if (age == null || age !in 18..120) {
            return ValidationResult(false, listOf("Age must be between 18 and 120"))
        }

        return ValidationResult(true, emptyList())
    }
}

// 5. Business Logic Validation
class BusinessValidators {

    // Validate payment amount
    fun validatePaymentAmount(amount: Double, balance: Double): Boolean {
        require(amount > 0) { "Payment amount must be positive" }
        require(amount <= balance) { "Insufficient funds: balance=$balance, amount=$amount" }

        return true
    }

    // Validate inventory
    fun validateInventory(itemId: String, quantity: Int, stock: Int): Boolean {
        require(quantity > 0) { "Quantity must be positive" }
        require(quantity <= stock) { "Insufficient stock: available=$stock, requested=$quantity" }

        return true
    }

    // Validate order
    fun validateOrder(items: Map<String, Int>, stock: Map<String, Int>): Boolean {
        require(items.isNotEmpty()) { "Order cannot be empty" }

        for ((itemId, quantity) in items) {
            val availableStock = stock[itemId] ?: 0
            require(quantity <= availableStock) {
                "Item $itemId: insufficient stock (available=$availableStock, requested=$quantity)"
            }
        }

        return true
    }

    // Validate shipping address
    data class Address(
        val street: String,
        val city: String,
        val state: String,
        val zipCode: String,
        val country: String
    )

    fun validateAddress(address: Address): Boolean {
        require(address.street.isNotBlank()) { "Street cannot be blank" }
        require(address.city.isNotBlank()) { "City cannot be blank" }
        require(address.state.isNotBlank()) { "State cannot be blank" }
        require(address.zipCode.isNotBlank()) { "Zip code cannot be blank" }
        require(address.country.isNotBlank()) { "Country cannot be blank" }

        // Validate zip code format (US example)
        val zipRegex = Regex("^\\d{5}(-\\d{4})?$")
        require(zipRegex.matches(address.zipCode)) { "Invalid zip code format" }

        return true
    }
}

// 6. Validation Utils
class ValidationUtils {

    // Generic range validator
    fun <T : Comparable<T>> validateRange(
                    value: T,
                    min: T,
                    max: T,
                    paramName: String
    ): T {
        require(value in min..max) {
            "$paramName must be between $min and $max, was: $value"
        }
        return value
    }

    // Validate not null
    fun <T> requireNotNull(value: T?, paramName: String): T {
        return requireNotNull(value) { "$paramName cannot be null" }
    }

    // Validate not blank
    fun requireNotBlank(value: String?, paramName: String): String {
        require(!value.isNullOrBlank()) { "$paramName cannot be blank" }
        return value
    }

    // Validate positive
    fun requirePositive(value: Int, paramName: String): Int {
        require(value > 0) { "$paramName must be positive, was: $value" }
        return value
    }

    fun requirePositive(value: Double, paramName: String): Double {
        require(value > 0) { "$paramName must be positive, was: $value" }
        return value
    }

    // Validate non-negative
    fun requireNonNegative(value: Int, paramName: String): Int {
        require(value >= 0) { "$paramName must be non-negative, was: $value" }
        return value
    }

    // Validate size
    fun <T> validateSize(
                    collection: Collection<T>,
                    minSize: Int,
                    maxSize: Int,
                    paramName: String
    ): Collection<T> {
        require(collection.size in minSize..maxSize) {
            "$paramName size must be between $minSize and $maxSize, was: ${collection.size}"
        }
        return collection
    }
}

// Main demonstration
fun demonstrateParameterValidation() {
    println("=== Android Kotlin Parameter Validation Examples ===\n")

    // 1. Basic validation
    println("--- 1. Basic Validation with require() ---")
    val basicValidation = BasicValidation()

    val discount = basicValidation.calculateDiscount(100.0, 15.0)
    println("Discounted price: $$discount")

    val username = basicValidation.createUsername("John", "Doe")
    println("Username: $username")

    try {
        basicValidation.setVolume(150)
    } catch (e: IllegalArgumentException) {
        println("Validation error: ${e.message}")
    }

    // 2. State validation
    println("\n--- 2. State Validation with check() ---")
    val stateValidation = StateValidation()

    stateValidation.initialize()
    stateValidation.connect()
    stateValidation.sendData("Hello")

    try {
        val stateValidation2 = StateValidation()
        stateValidation2.sendData("Test")
    } catch (e: IllegalStateException) {
        println("State error: ${e.message}")
    }

    // 3. Custom validators
    println("\n--- 3. Custom Validators ---")
    val customValidators = CustomValidators()

    val email = "[email protected]"
    println("Is valid email: ${customValidators.isValidEmail(email)}")

    val phone = "+1234567890"
    println("Is valid phone: ${customValidators.isValidPhoneNumber(phone)}")

    val url = "https://example.com"
    println("Is valid URL: ${customValidators.isValidUrl(url)}")

    val passwordStrength = customValidators.checkPasswordStrength("Pass123!")
    println("Password strength: $passwordStrength")

    // 4. Validation collector
    println("\n--- 4. Validation Collector ---")
    val collector = ValidationCollector()

    val userData = mapOf(
        "name" to "John Doe",
        "age" to 25,
        "email" to "[email protected]"
    )

    val result = collector.validateUser(userData)
    println("Validation result: ${result.isValid}")
    if (!result.isValid) {
        println("Errors: ${result.getErrorString()}")
    }

    // 5. Business validation
    println("\n--- 5. Business Logic Validation ---")
    val businessValidators = BusinessValidators()

    val paymentValid = businessValidators.validatePaymentAmount(50.0, 100.0)
    println("Payment valid: $paymentValid")

    val address = BusinessValidators.Address(
        street = "123 Main St",
        city = "New York",
        state = "NY",
        zipCode = "10001",
        country = "USA"
    )

    val addressValid = businessValidators.validateAddress(address)
    println("Address valid: $addressValid")

    // 6. Validation utils
    println("\n--- 6. Validation Utils ---")
    val utils = ValidationUtils()

    val validRange = utils.validateRange(50, 0, 100, "value")
    println("Validated range: $validRange")

    val notNull = utils.requireNotNull("value", "param")
    println("Not null: $notNull")

    val positive = utils.requirePositive(10, "count")
    println("Positive: $positive")

    val list = listOf(1, 2, 3)
    val validList = utils.validateSize(list, 1, 5, "items")
    println("Validated list size: ${validList.size}")

    println("\n=== All Parameter Validation Examples Completed ===")
}

💻 Exception Catching kotlin

🟡 intermediate ⭐⭐⭐

Handle exceptions using try-catch blocks, custom exceptions, and error recovery strategies

⏱️ 25 min 🏷️ kotlin, android, error handling
Prerequisites: Intermediate Kotlin
// Android Kotlin Exception Catching Examples
// Using Kotlin exception handling mechanisms

// 1. Basic Exception Handling
class BasicExceptionHandling {

    // Simple try-catch
    fun divideNumbers(a: Int, b: Int): Double? {
        return try {
            (a.toDouble() / b.toDouble()).also {
                println("Result: $it")
            }
        } catch (e: ArithmeticException) {
            println("Division by zero: ${e.message}")
            null
        } catch (e: Exception) {
            println("Unexpected error: ${e.message}")
            null
        }
    }

    // Try-catch-finally
    fun processFile(filePath: String): String? {
        var content: String? = null

        try {
            val file = java.io.File(filePath)
            content = file.readText()
            println("File read successfully")
        } catch (e: java.io.FileNotFoundException) {
            println("File not found: ${e.message}")
        } catch (e: java.io.IOException) {
            println("IO error: ${e.message}")
        } catch (e: Exception) {
            println("Unexpected error: ${e.message}")
        } finally {
            println("File processing attempt completed")
        }

        return content
    }

    // Try-catch with resource cleanup
    fun processFileWithCleanup(filePath: String): String? {
        val file = java.io.File(filePath)

        return try {
            if (file.exists()) {
                file.readText().also {
                    println("Content length: ${it.length}")
                }
            } else {
                println("File does not exist")
                null
            }
        } catch (e: Exception) {
            println("Error processing file: ${e.message}")
            null
        }
    }

    // Nested try-catch
    fun nestedExceptionHandling() {
        try {
            println("Outer try block")
            try {
                println("Inner try block")
                val result = 10 / 0
            } catch (e: ArithmeticException) {
                println("Inner catch: ${e.message}")
                throw RuntimeException("Re-throwing from inner")
            }
        } catch (e: RuntimeException) {
            println("Outer catch: ${e.message}")
        }
    }
}

// 2. Custom Exceptions
class CustomExceptions {

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

    class NetworkException(message: String, val statusCode: Int? = null) : Exception(message)

    class DatabaseException(message: String, val errorCode: String) : Exception(message)

    // Throw and catch custom exception
    fun validateAge(age: Int) {
        if (age < 0) {
            throw ValidationException("Age cannot be negative: $age")
        }
        if (age > 150) {
            throw ValidationException("Age exceeds maximum: $age")
        }
        println("Age $age is valid")
    }

    // Handle custom exception
    fun processUserAge(age: Int): Boolean {
        return try {
            validateAge(age)
            true
        } catch (e: ValidationException) {
            println("Validation failed: ${e.message}")
            false
        }
    }

    // Network operation with custom exception
    fun fetchFromServer(endpoint: String): String? {
        if (endpoint.isEmpty()) {
            throw NetworkException("Endpoint cannot be empty", null)
        }

        // Simulate network call
        return try {
            // Simulated response
            "Data from $endpoint"
        } catch (e: Exception) {
            throw NetworkException("Network error: ${e.message}", 500)
        }
    }

    // Handle network exception
    fun safeFetch(endpoint: String): String? {
        return try {
            fetchFromServer(endpoint)
        } catch (e: NetworkException) {
            println("Network error (status ${e.statusCode}): ${e.message}")
            null
        }
    }

    // Multiple custom exceptions
    fun databaseOperation(operation: String): Boolean {
        return try {
            when (operation) {
                "connect" -> true
                "query" -> true
                else -> throw DatabaseException("Unknown operation: $operation", "OP_UNKNOWN")
            }
        } catch (e: DatabaseException) {
            println("DB error [${e.errorCode}]: ${e.message}")
            false
        }
    }
}

// 3. Exception Chaining
class ExceptionChaining {

    // Exception chaining with cause
    fun loadDataFromFile(filePath: String): String {
        return try {
            val file = java.io.File(filePath)
            file.readText()
        } catch (e: java.io.IOException) {
            throw RuntimeException("Failed to load data from file: $filePath", e)
        }
    }

    // Catch and re-throw with additional context
    fun processData(filePath: String): Int {
        return try {
            val data = loadDataFromFile(filePath)
            data.length
        } catch (e: RuntimeException) {
            throw RuntimeException("Failed to process data from: $filePath", e)
        }
    }

    // Handle chained exception
    fun safeProcessData(filePath: String): Int? {
        return try {
            processData(filePath)
        } catch (e: RuntimeException) {
            println("Error: ${e.message}")
            println("Cause: ${e.cause?.message}")

            // Print stack trace of cause
            e.cause?.printStackTrace()

            null
        }
    }

    // Add suppression
    @Suppress("TooGenericExceptionCaught")
    fun handleWithSuppression(): String? {
        return try {
            // Some operation
            "Success"
        } catch (e: Exception) {
            println("Caught exception: ${e.message}")
            null
        }
    }
}

// 4. Kotlin-specific Exception Handling
class KotlinExceptionHandling {

    // Result type for error handling
    fun divideSafely(a: Int, b: Int): Result<Double> {
        return try {
            Result.success(a.toDouble() / b.toDouble())
        } catch (e: ArithmeticException) {
            Result.failure(e)
        }
    }

    // Use Result type
    fun performDivision(): String? {
        val result = divideSafely(10, 0)

        return result.fold(
            onSuccess = { value ->
                println("Result: $value")
                value.toString()
            },
            onFailure = { error ->
                println("Error: ${error.message}")
                null
            }
        )
    }

    // Nullable return type
    fun parseToInt(value: String): Int? {
        return try {
            value.toInt()
        } catch (e: NumberFormatException) {
            println("Invalid number: $value")
            null
        }
    }

    // Elvis operator with exception
    fun getNameOrThrow(map: Map<String, String?>): String {
        return map["name"] ?: throw IllegalArgumentException("Name is required")
    }

    // Require for validation
    fun processAmount(amount: Double) {
        require(amount > 0) { "Amount must be positive, was: $amount" }
        require(amount <= 1_000_000) { "Amount exceeds maximum" }
        println("Amount processed: $$amount")
    }

    // Check for state validation
    fun processPayment(balance: Double, amount: Double) {
        check(balance >= amount) { "Insufficient funds: balance=$balance, required=$amount" }
        println("Payment processed")
    }

    // RunCatching
    fun safeOperation(): Int? {
        val result = runCatching {
            "10".toInt() * 2
        }

        return result.getOrNull()
    }

    // Multiple operations with runCatching
    fun chainOperations(): String? {
        val result = runCatching {
            val num = "10".toInt()
            val doubled = num * 2
            "Result: $doubled"
        }.onFailure { e ->
            println("Operation failed: ${e.message}")
        }

        return result.getOrNull()
    }
}

// 5. Advanced Error Recovery
class ErrorRecovery {

    // Retry mechanism
    fun <T> retryOperation(
                    operation: () -> T,
                    maxAttempts: Int = 3,
                    delayMs: Long = 1000
    ): T? {
        var lastException: Exception? = null

        repeat(maxAttempts) { attempt ->
            try {
                return operation()
            } catch (e: Exception) {
                lastException = e
                println("Attempt ${attempt + 1} failed: ${e.message}")

                if (attempt < maxAttempts - 1) {
                    Thread.sleep(delayMs)
                }
            }
        }

        println("All $maxAttempts attempts failed")
        return null
    }

    // Fallback strategy
    fun <T> tryWithFallback(
                    primary: () -> T,
                    fallback: () -> T
    ): T {
        return try {
            primary()
        } catch (e: Exception) {
            println("Primary failed, using fallback: ${e.message}")
            fallback()
        }
    }

    // Multiple fallbacks
    fun <T> tryMultipleFallbacks(
                    operations: List<Pair<String, () -> T>>
    ): T? {
        for ((name, operation) in operations) {
            try {
                val result = operation()
                println("Success with: $name")
                return result
            } catch (e: Exception) {
                println("Failed with $name: ${e.message}")
            }
        }

        println("All operations failed")
        return null
    }

    // Circuit breaker pattern (simplified)
    class CircuitBreaker<T>(
                    private val operation: () -> T,
                    private val failureThreshold: Int = 3
    ) {
        private var failureCount = 0
        private var lastFailureTime: Long = 0
        private var state = State.CLOSED

        enum class State { CLOSED, OPEN, HALF_OPEN }

        fun execute(): T? {
            if (state == State.OPEN) {
                val timeSinceFailure = System.currentTimeMillis() - lastFailureTime

                if (timeSinceFailure > 60000) { // 1 minute cooldown
                    state = State.HALF_OPEN
                    println("Circuit breaker entering HALF_OPEN state")
                } else {
                    println("Circuit breaker is OPEN, rejecting request")
                    return null
                }
            }

            return try {
                val result = operation()

                if (state == State.HALF_OPEN) {
                    state = State.CLOSED
                    failureCount = 0
                    println("Circuit breaker reset to CLOSED")
                }

                result
            } catch (e: Exception) {
                failureCount++
                lastFailureTime = System.currentTimeMillis()

                if (failureCount >= failureThreshold) {
                    state = State.OPEN
                    println("Circuit breaker opened after $failureCount failures")
                }

                throw e
            }
        }
    }

    // Execute with circuit breaker
    fun <T> executeWithCircuitBreaker(operation: () -> T): T? {
        val circuitBreaker = CircuitBreaker(operation)

        return try {
            circuitBreaker.execute()
        } catch (e: Exception) {
            println("Circuit breaker operation failed: ${e.message}")
            null
        }
    }
}

// 6. Exception Logging
class ExceptionLogger {

    // Log exception with context
    fun logException(context: String, exception: Exception) {
        println("[$context] Exception: ${exception.javaClass.simpleName}")
        println("[$context] Message: ${exception.message}")
        println("[$context] Stack trace:")

        exception.stackTrace.forEach { element ->
            println("[$context]   at $element")
        }
    }

    // Log with custom severity
    enum class Severity { ERROR, WARNING, INFO }

    fun logWithSeverity(
                    severity: Severity,
                    message: String,
                    exception: Exception? = null
    ) {
        val timestamp = java.util.Date()
        println("[$timestamp] [$severity] $message")

        exception?.let {
            println("[$timestamp] [$severity] Caused by: ${it.message}")
        }
    }

    // Collect exception details
    fun captureExceptionDetails(exception: Exception): ExceptionDetails {
        return ExceptionDetails(
            type = exception.javaClass.simpleName,
            message = exception.message,
            cause = exception.cause?.message,
            stackTrace = exception.stackTrace.joinToString("\n") { "  at $it" }
        )
    }

    // Exception details data class
    data class ExceptionDetails(
        val type: String,
        val message: String?,
        val cause: String?,
        val stackTrace: String
    )
}

// Main demonstration
fun demonstrateExceptionCatching() {
    println("=== Android Kotlin Exception Catching Examples ===\n")

    // 1. Basic exception handling
    println("--- 1. Basic Exception Handling ---")
    val basicHandler = BasicExceptionHandling()

    basicHandler.divideNumbers(10, 2)
    basicHandler.divideNumbers(10, 0)

    basicHandler.processFile("nonexistent.txt")

    // 2. Custom exceptions
    println("\n--- 2. Custom Exceptions ---")
    val customExceptions = CustomExceptions()

    val ages = listOf(25, -5, 200, 30)
    ages.forEach { age ->
        val isValid = customExceptions.processUserAge(age)
        println("Age $age valid: $isValid")
    }

    // 3. Exception chaining
    println("\n--- 3. Exception Chaining ---")
    val chaining = ExceptionChaining()

    chaining.safeProcessData("nonexistent.txt")

    // 4. Kotlin-specific handling
    println("\n--- 4. Kotlin-Specific Handling ---")
    val kotlinHandler = KotlinExceptionHandling()

    kotlinHandler.performDivision()

    kotlinHandler.parseToInt("123")
    kotlinHandler.parseToInt("abc")

    // 5. Error recovery
    println("\n--- 5. Error Recovery ---")
    val recovery = ErrorRecovery()

    var attempts = 0
    val result = recovery.retryOperation(
        operation = {
            attempts++
            if (attempts < 3) {
                throw RuntimeException("Simulated failure")
            }
            "Success after $attempts attempts"
        },
        maxAttempts = 5
    )

    println("Retry result: $result")

    // 6. Fallback
    val fallbackResult = recovery.tryWithFallback(
        primary = { throw RuntimeException("Primary failed") },
        fallback = { "Fallback value" }
    )

    println("Fallback result: $fallbackResult")

    // 7. Exception logging
    println("\n--- 6. Exception Logging ---")
    val logger = ExceptionLogger()

    try {
        throw RuntimeException("Test exception")
    } catch (e: Exception) {
        logger.logException("TestContext", e)
    }

    logger.logWithSeverity(ExceptionLogger.Severity.ERROR, "Critical error occurred")

    println("\n=== All Exception Catching Examples Completed ===")
}