Sobes.tech
Junior

Como tratar corretamente as exceções em Kotlin?

sobes.tech IA

Resposta da IA

Em Kotlin, para lidar com exceções, utiliza-se a construção try...catch...finally. Pode-se chamar funções marcadas com @Throws para interagir com código Java.

fun readFile(path: String): String {
    return try {
        // Tenta executar um código potencialmente perigoso
        java.io.File(path).readText()
    } catch (e: java.io.IOException) {
        // Lida com uma exceção específica
        println("Erro ao ler o arquivo: ${e.message}")
        "" // Retorna um valor padrão em caso de erro
    } finally {
        // Bloco que sempre é executado, independentemente da exceção
        println("Leitura do arquivo concluída")
    }
}

É possível usar vários blocos catch para tratar diferentes tipos de exceções:

fun processData(data: String) {
    try {
        // Código que pode lançar NumberFormatException ou 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("Dados nulos recebidos: ${e.message}")
    }
}

Kotlin não possui exceções verificadas como em Java. As funções podem declarar que podem lançar exceções usando @Throws, mas isso não é obrigatório em código Kotlin.

Para casos em que uma função pode terminar com sucesso ou erro, frequentemente usam-se classes seladas (sealed classes) ou o tipo Result (em versões experimentais do Kotlin) em vez de exceções, especialmente em código assíncrono.

Exemplo com classe selada:

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("Divisão por zero")
        }
        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("Erro: ${divisionResult.exception.message}")
    }
}

É importante capturar apenas as exceções que você pode tratar. Não é recomendado deixar blocos catch vazios. Se necessário, pode-se registrar a exceção em um log.

Também é possível lançar exceções explicitamente usando a palavra-chave throw:

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