Sobes.tech
Junior

Come gestire correttamente le eccezioni in Kotlin?

sobes.tech AI

Risposta dell'AI

In Kotlin, la gestione delle eccezioni avviene tramite la costruzione try...catch...finally. È possibile chiamare funzioni annotate con @Throws per interagire con codice Java.

fun readFile(path: String): String {
    return try {
        // Tentativo di eseguire codice rischioso
        java.io.File(path).readText()
    } catch (e: java.io.IOException) {
        // Gestione di un'eccezione specifica
        println("Errore durante la lettura del file: ${e.message}")
        "" // Valore di default in caso di errore
    } finally {
        // Blocco che viene sempre eseguito, indipendentemente dall'eccezione
        println("Lettura del file completata")
    }
}

È possibile usare più blocchi catch per gestire diversi tipi di eccezioni:

fun processData(data: String) {
    try {
        // Codice che può generare NumberFormatException o NullPointerException
        val number = data.toInt()
        println("Numero: $number")
    } catch (e: NumberFormatException) {
        println("Formato numero non valido: ${e.message}")
    } catch (e: NullPointerException) {
        println("Dati nulli ricevuti: ${e.message}")
    }
}

Kotlin non ha eccezioni checked come Java. Le funzioni possono dichiarare che possono lanciare eccezioni usando @Throws, ma non è obbligatorio.

Per casi in cui una funzione può terminare con successo o errore, si usano spesso classi sealed o il tipo Result (nelle versioni sperimentali di Kotlin) invece delle eccezioni, specialmente nel codice asincrono.

Esempio con classe sealed:

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("Divisione per zero")
        }
        Success(a / b)
    } catch (e: Throwable) {
        Error(e)
    }
}

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

È importante catturare solo le eccezioni che si possono gestire. Evitare blocchi catch vuoti. Se necessario, si può registrare l'eccezione nel log.

È anche possibile lanciare eccezioni esplicitamente usando la parola chiave throw:

fun requirePositive(number: Int) {
    if (number <= 0) {
        throw IllegalArgumentException("Il numero deve essere positivo")
    }
}