Kotlin Exception Handling Utility - Clean Logging, Centralized Catching

Kotlin Exception Handling: Tame Errors Like a Pro in 2025! 

Handle runtime issues with Kotlin’s exception handling. This 2025 guide provides practical examples, a utility class, and tips to make your code crash-proof! 

Key Constructs 

  • ๐Ÿ” try: Wraps risky code, paired with catch or finally.
  • ๐Ÿ›ก️ catch: Handles specific exceptions from try.
  • finally: Runs cleanup tasks, exception or not.
  • ๐Ÿ’ฅ throw: Triggers custom exceptions.
Exception Handling Flow

Exception Types ⚠️๐Ÿ”

Kotlin exceptions are unchecked, inheriting from Throwable (Error or Exception). Key types:

  • ArithmeticException: Division by zero.
  • ๐Ÿ“ ArrayIndexOutOfBoundsException: Invalid array index.
  • ๐Ÿ”„ ClassCastException: Invalid type casting.
  • ๐Ÿ“ FileNotFoundException: Non-existent file access.
  • ๐Ÿ’พ IOException: Input/output errors.
  • ⏸️ InterruptedException: Thread interruption.
  • ๐Ÿšซ NullPointerException: Null object access.
  • ๐Ÿ”’ SecurityException: Security violations.
  • ๐Ÿ”ข NumberFormatException: Invalid number conversion.
  • ๐Ÿ“‰ IndexOutOfBoundsException: Invalid list/array index.
  • ๐ŸŒ RemoteException: Remote service errors.
  • ⚠️ IllegalStateException: Invalid state operations.
  • ๐Ÿšซ UnsupportedOperationException: Unsupported operations.
  • ๐Ÿ’ฅ RuntimeException: General runtime errors.
  • ๐Ÿ” NoSuchElementException: Missing collection elements.
  • ๐Ÿ”„ ConcurrentModificationException: Collection modified during iteration.

ExceptionUtils: Logging Utility ๐Ÿž๐Ÿ“

Log exceptions consistently with ExceptionUtils.


try {
    val str: String? = null
    val length = str!!.length // ๐Ÿšซ NullPointerException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "NullCheck: Missing string")
}
        

Log Output:

๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž
๐Ÿž Feature   : NullCheck: Missing string
๐Ÿž Exception : ๐Ÿšซ NullPointerException
๐Ÿž Message   : null
๐Ÿž Method    : someMethod
๐Ÿž Line no   : 123
๐Ÿž File name : (SomeFile.kt:123)
        

Multiple Catch Blocks ๐Ÿ›ก️๐Ÿ”„

Handle different exceptions with multiple catch blocks.


fun main() {
    try {
        val a = IntArray(5)
        a[5] = 10 / 0 // ๐Ÿ“ ArrayIndexOutOfBoundsException
    } catch (e: ArithmeticException) {
        println("Arithmetic exception caught")
    } catch (e: ArrayIndexOutOfBoundsException) {
        println("Array index out of bounds caught") // ๐Ÿ“œ Output
    } catch (e: Exception) {
        println("General exception caught")
    }
}
        

Nested Try-Catch Blocks ๐Ÿ›ก️๐Ÿ”—

Handle layered risks with nested try-catch.


fun main() {
    val nume = intArrayOf(4, 8, 16, 32, 64, 128, 256, 512)
    val deno = intArrayOf(2, 0, 4, 4, 0, 8)
    try {
        for (i in nume.indices) {
            try {
                println("${nume[i]} / ${deno[i]} is ${nume[i] / deno[i]}") // ➗ ArithmeticException
            } catch (exc: ArithmeticException) {
                println("Can't divide by zero!") // ๐Ÿ“œ Output
            }
        }
    } catch (exc: ArrayIndexOutOfBoundsException) {
        println("Element not found.") // ๐Ÿ“œ Output
    }
}
        

Finally Block ✅๐Ÿงน

Cleanup with finally, runs regardless of exceptions.


fun main() {
    try {
        val data = 10 / 5
        println(data) // ๐Ÿ“œ Output: 2
    } catch (e: NullPointerException) {
        println(e)
    } finally {
        println("Finally block always executes") // ๐Ÿ“œ Output
    }
}
        

Throw Keyword ๐Ÿ’ฅ๐Ÿšจ

Trigger custom exceptions with throw.


fun main() {
    try {
        validate(15)
        println("Code after validation check...")
    } catch (e: ArithmeticException) {
        println("Caught: ${e.message}") // ๐Ÿ“œ Output: Caught: under age
    }
}

fun validate(age: Int) {
    if (age < 18) {
        throw ArithmeticException("under age") // ๐Ÿ’ฅ
    } else {
        println("Eligible to drive")
    }
}
        

Try as an Expression ๐Ÿง ๐Ÿ”„

Use try as an expression for functional error handling.


fun parseNumber(input: String?): Int = try {
    input?.toInt() ?: throw IllegalArgumentException("Input is null")
} catch (e: NumberFormatException) {
    println("Invalid number: $input")
    -1
} catch (e: IllegalArgumentException) {
    println("Error: ${e.message}")
    -2
}

fun main() {
    println(parseNumber("123")) // ๐Ÿ“œ Output: 123
    println(parseNumber("abc")) // ๐Ÿ“œ Output: Invalid number: abc, -1
    println(parseNumber(null)) // ๐Ÿ“œ Output: Error: Input is null, -2
}
        

Resource Management with use ๐Ÿงน⚙️

Auto-close resources with use.


import java.io.BufferedReader
import java.io.StringReader

fun readFirstLine(fileName: String?): String? = try {
    BufferedReader(StringReader(fileName ?: "")).use { reader ->
        reader.readLine()
    }
} catch (e: java.io.IOException) {
    println("IO Error: ${e.message}")
    null
}

fun main() {
    println(readFirstLine("Hello, Kotlin!")) // ๐Ÿ“œ Output: Hello, Kotlin!
    println(readFirstLine(null)) // ๐Ÿ“œ Output: null
}
        

ExceptionUtils Class ๐Ÿž๐Ÿ“


package com.boltuix.androidmasterypro

import android.os.RemoteException
import android.util.Log
import java.io.FileNotFoundException
import java.io.IOException
import java.io.PrintWriter
import java.io.StringWriter

/**
 * Utility for logging exceptions with emojis.
 *
 * Usage:
 * ```
 * try {
 *     // Risky code
 * } catch (e: Exception) {
 *     ExceptionUtils.handleException(e, "FeatureName: Description")
 * }
 * ```
 */
object ExceptionUtils {

    private val exceptionTypeMap = mapOf(
        ArithmeticException::class.java to Pair("ArithmeticException", "➗"),
        ArrayIndexOutOfBoundsException::class.java to Pair("ArrayIndexOutOfBoundsException", "๐Ÿ“"),
        ClassCastException::class.java to Pair("ClassCastException", "๐Ÿ”„"),
        FileNotFoundException::class.java to Pair("FileNotFoundException", "๐Ÿ“"),
        IOException::class.java to Pair("IOException", "๐Ÿ’พ"),
        InterruptedException::class.java to Pair("InterruptedException", "⏸️"),
        NullPointerException::class.java to Pair("NullPointerException", "๐Ÿšซ"),
        SecurityException::class.java to Pair("SecurityException", "๐Ÿ”’"),
        NumberFormatException::class.java to Pair("NumberFormatException", "๐Ÿ”ข"),
        IndexOutOfBoundsException::class.java to Pair("IndexOutOfBoundsException", "๐Ÿ“‰"),
        RemoteException::class.java to Pair("RemoteException", "๐ŸŒ"),
        IllegalStateException::class.java to Pair("IllegalStateException", "⚠️"),
        UnsupportedOperationException::class.java to Pair("UnsupportedOperationException", "๐Ÿšซ"),
        RuntimeException::class.java to Pair("RuntimeException", "๐Ÿ’ฅ"),
        NoSuchElementException::class.java to Pair("NoSuchElementException", "๐Ÿ”"),
        ConcurrentModificationException::class.java to Pair("ConcurrentModificationException", "๐Ÿ”„")
    )

    /**
     * Retrieves supported exception types with names and emojis.
     *
     * @return List of triples (class, name, emoji).
     */
    fun getSupportedExceptions(): List<Triple<Class<out Exception>, String, String>> {
        return exceptionTypeMap.map { (clazz, pair) ->
            Triple(clazz, pair.first, pair.second)
        }
    }

    /**
     * Logs a message to the console (in debug mode).
     *
     * @param message The message to log.
     * @param level Log level (default: ERROR).
     */
    private fun logMessage(message: String, level: String = "ERROR") {
        if (!isDebugMode()) return
        val tag = "error01"
        when (level) {
            "ERROR" -> Log.e(tag, message)
            "DEBUG" -> Log.d(tag, message)
            "WARN" -> Log.w(tag, message)
        }
    }

    /**
     * Handles an exception and logs it with an emoji (in debug mode).
     *
     * @param e The exception to handle.
     * @param customMessage A descriptive message for the feature or context.
     */
    fun handleException(e: Exception, customMessage: String) {
        if (!isDebugMode()) return
        try {
            val (errorMessage, emoji) = exceptionTypeMap[e::class.java] ?: Pair("GenericException", "❓")
            val stackElement = e.stackTrace.firstOrNull { it.className.contains("com.boltuix.androidmasterypro") }

            val methodName = stackElement?.methodName ?: "Unknown"
            val lineNumber = stackElement?.lineNumber?.toString() ?: "Unknown"
            val fileName = stackElement?.fileName ?: "Unknown"

            val stackTrace = if (e.message == null) {
                StringWriter().also { sw ->
                    PrintWriter(sw).use { pw -> e.printStackTrace(pw) }
                }.toString()
            } else {
                ""
            }

            val logMessage = StringBuilder().apply {
                appendLine("๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž ๐Ÿž")
                appendLine("๐Ÿž Feature   : $customMessage")
                appendLine("๐Ÿž Exception : $emoji $errorMessage")
                appendLine("๐Ÿž Message   : ${e.message ?: "No message"}")
                appendLine("๐Ÿž Method    : $methodName")
                appendLine("๐Ÿž Line no   : $lineNumber")
                appendLine("๐Ÿž File name : ($fileName:$lineNumber)")
                if (stackTrace.isNotEmpty()) appendLine("๐Ÿž Stack trace : $stackTrace")
                appendLine()
            }.toString()

            logMessage(logMessage)
        } catch (e: Exception) {
            logMessage("Error handling exception: ${e.message} | Context: $customMessage")
        }
    }

    /**
     * Checks if the application is in debug mode.
     *
     * @return True if debug mode is enabled, false otherwise.
     */
    private fun isDebugMode(): Boolean = BuildConfig.DEBUG
}
        

ExceptionUtils Usage Demo ๐Ÿ› ️


try {
    val arr = IntArray(5)
    val value = arr[10] // ๐Ÿ“ ArrayIndexOutOfBoundsException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test1: Array Index Out of Bounds Exception")
}

try {
    val a: Any = "Hello"
    val num = a as Int // ๐Ÿ”„ ClassCastException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test2: Class Cast Exception")
}

try {
    val file = java.io.File("non_existent_file.txt")
    val reader = java.io.FileReader(file) // ๐Ÿ“ FileNotFoundException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test3: File Not Found Exception")
}

try {
    val inputStream = java.io.FileInputStream("non_existent_file.txt")
    val data = inputStream.read() // ๐Ÿ’พ IOException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test4: I/O Exception")
}

try {
    Thread.sleep(1000)
    throw InterruptedException("Thread interrupted") // ⏸️ InterruptedException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test5: Interrupted Exception")
}

try {
    val str: String? = null
    val length = str!!.length // ๐Ÿšซ NullPointerException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test6: Null Pointer Exception")
}

try {
    System.setSecurityManager(SecurityManager()) // ๐Ÿ”’ SecurityException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test7: Security Exception")
}

try {
    val str = "abc"
    val num = str.toInt() // ๐Ÿ”ข NumberFormatException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test8: Number Format Exception")
}

try {
    throw RemoteException() // ๐ŸŒ RemoteException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test9: Remote Exception")
}

try {
    throw IllegalStateException("Illegal state") // ⚠️ IllegalStateException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test10: Illegal State Exception")
}

try {
    val num = 10 / 0 // ➗ ArithmeticException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test11: Arithmetic Exception")
}

try {
    val list = listOf(1, 2, 3)
    list[10] // ๐Ÿ“‰ IndexOutOfBoundsException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test12: Index Out of Bounds Exception")
}

try {
    throw UnsupportedOperationException("Operation not supported") // ๐Ÿšซ UnsupportedOperationException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test13: Unsupported Operation Exception")
}

try {
    throw RuntimeException("Runtime error") // ๐Ÿ’ฅ RuntimeException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test14: Runtime Exception")
}

try {
    val iterator = listOf<Int>().iterator()
    iterator.next() // ๐Ÿ” NoSuchElementException
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test15: No Such Element Exception")
}

try {
    val list = mutableListOf(1, 2, 3)
    for (item in list) {
        list.remove(item) // ๐Ÿ”„ ConcurrentModificationException
    }
} catch (e: Exception) {
    ExceptionUtils.handleException(e, "Test16: Concurrent Modification Exception")
}
        

Last Updated: June 27, 2025

Post a Comment

Previous Post Next Post