Sobes.tech
Junior

Jak poprawnie obsługiwać wyjątki w Kotlinie?

sobes.tech AI

Odpowiedź od AI

W Kotlinie obsługa wyjątków odbywa się za pomocą konstrukcji try...catch...finally. Możesz wywoływać funkcje oznaczone adnotacją @Throws, aby współpracować z kodem Java.

fun readFile(path: String): String {
    return try {
        // Próba wykonania niebezpiecznego kodu
        java.io.File(path).readText()
    } catch (e: java.io.IOException) {
        // Obsługa konkretnego wyjątku
        println("Błąd podczas odczytu pliku: ${e.message}")
        "" // Domyślna wartość w przypadku błędu
    } finally {
        // Blok, który zawsze się wykonuje, niezależnie od wyjątku
        println("Odczyt pliku zakończony")
    }
}

Możesz użyć kilku bloków catch, aby obsłużyć różne typy wyjątków:

fun processData(data: String) {
    try {
        // Kod, który może wywołać NumberFormatException lub NullPointerException
        val number = data.toInt()
        println("Liczba: $number")
    } catch (e: NumberFormatException) {
        println("Nieprawidłowy format liczby: ${e.message}")
    } catch (e: NullPointerException) {
        println("Odebrane dane null: ${e.message}")
    }
}

Kotlin nie posiada checked-ekscepcji jak w Javie. Funkcje mogą deklarować, że mogą rzucać wyjątki za pomocą @Throws, ale nie jest to obowiązkowe.

W przypadkach, gdy funkcja może zakończyć się sukcesem lub błędem, często stosuje się klasy sealed lub typ Result (w eksperymentalnych wersjach Kotlin), zamiast wyjątków, szczególnie w kodzie asynchronicznym.

Przykład z klasą 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("Dzielenie przez zero")
        }
        Success(a / b)
    } catch (e: Throwable) {
        Error(e)
    }
}

// Użycie:
fun processDivision() {
    when (val divisionResult = safelyDivide(10, 0)) {
        is Success -> println("Wynik: ${divisionResult.value}")
        is Error -> println("Błąd: ${divisionResult.exception.message}")
    }
}

Ważne jest, aby przechwytywać tylko te wyjątki, które można obsłużyć. Nie należy zostawiać pustych bloków catch. W razie potrzeby można zarejestrować informacje o wyjątku w logu.

Możesz także jawnie rzucać wyjątki za pomocą słowa kluczowego throw:

fun requirePositive(number: Int) {
    if (number <= 0) {
        throw IllegalArgumentException("Liczba musi być dodatnia")
    }
}