Sobes.tech
Junior

¿Cómo manejar correctamente las excepciones en Kotlin?

sobes.tech AI

Respuesta de la IA

En Kotlin, para manejar excepciones se utiliza la construcción try...catch...finally. Se pueden llamar funciones marcadas con @Throws para interactuar con código Java.

fun readFile(path: String): String {
    return try {
        // Intento de ejecutar código peligroso
        java.io.File(path).readText()
    } catch (e: java.io.IOException) {
        // Manejo de una excepción específica
        println("Error al leer el archivo: ${e.message}")
        "" // Valor predeterminado en caso de error
    } finally {
        // Bloque que siempre se ejecuta, independientemente de la excepción
        println("Lectura de archivo finalizada")
    }
}

Se pueden usar varios bloques catch para manejar diferentes tipos de excepciones:

fun processData(data: String) {
    try {
        // Código que puede lanzar NumberFormatException o NullPointerException
        val number = data.toInt()
        println("Número: $number")
    } catch (e: NumberFormatException) {
        println("Formato de número inválido: ${e.message}")
    } catch (e: NullPointerException) {
        println("Datos nulos recibidos: ${e.message}")
    }
}

Kotlin no tiene excepciones verificadas como en Java. Las funciones pueden declarar que pueden lanzar excepciones usando @Throws, pero esto no es obligatorio en código Kotlin.

Para casos donde una función puede terminar con éxito o con error, a menudo se usan clases selladas (sealed classes) o el tipo Result (en versiones experimentales de Kotlin) en lugar de excepciones, especialmente en código asíncrono.

Ejemplo con clase sellada:

sealed class Result<out T>
data class Success<out T>(val value: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()

fun safelyDivide(a: Int, b: Int): Result<Int> {
    return try {
        if (b == 0) {
            throw ArithmeticException("División por cero")
        }
        Success(a / b)
    } catch (e: Throwable) {
        Error(e)
    }
}

// Uso:
fun processDivision() {
    when (val divisionResult = safelyDivide(10, 0)) {
        is Success -> println("Resultado: ${divisionResult.value}")
        is Error -> println("Error: ${divisionResult.exception.message}")
    }
}

Es importante capturar solo las excepciones que se pueden manejar. No se recomienda dejar bloques catch vacíos. Si es necesario, se puede registrar la excepción en un log.

También se pueden lanzar excepciones explícitamente usando la palabra clave throw:

fun requirePositive(number: Int) {
    if (number <= 0) {
        throw IllegalArgumentException("El número debe ser positivo")
    }
}