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

Comments

Popular posts from this blog

Creating Beautiful Card UI in Flutter

Master Web Development with Web School Offline

Jetpack Compose - Card View