programowanieProgramista Backend (Kotlin)

Jak działa obsługa wyjątków (Exception Handling) w Kotlin? Czym różni się podejście Kotlin od Java, jak obsługiwać wyjątki, jakie są niuanse związane z checked/unchecked exception, jak wpływa użycie try/catch/finally?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

W Kotlin obsługa wyjątków jest realizowana za pomocą konstrukcji try/catch/finally, ale różni się od podejścia Java skupieniem na Unchecked Exceptions i zwięzłą składnią. Ważna różnica polega na tym, że w Kotlinie brak jest checked exceptions, co odciąża programistę od obowiązkowego wskazywania throws i przechwytywania takich wyjątków.

Historia pytania

Java używa checked/unchecked exceptions, gdzie checked wymagają jawnego przechwytywania lub deklaracji. Często prowadzi to do nadmiarowego boilerplate'u i kłopotliwego kodu. Kotlin zaprojektowano prościej — wszystkie wyjątki są zazwyczaj unchecked (dziedziczące po RuntimeException), co ułatwia obsługę i czyni kod bardziej czytelnym.

Problem

Programiści Java w Kotlinie próbują przechwytywać i wskazywać checked exceptions — kod kompiluje się, ale sens się zatraca. Ponadto, użycie finally bez świadomości niuansów prowadzi do wycieków lub nie działającego kodu.

Rozwiązanie

Używamy try/catch/finally w Kotlin w sposób zwięzły i rzeczowy:

fun process(data: String): Int = try { data.toInt() } catch (e: NumberFormatException) { 0 } finally { println("Processing done") }
  • try/catch/finally może być wyrażeniem, co oznacza, że zwraca wartość, która staje się końcowym wynikiem funkcji lub jest przypisywana do zmiennej.
  • Można przechwytywać wiele wyjątków, używać przełączania when wewnątrz catch dla różnej reakcji na typy wyjątków.

Kluczowe cechy:

  • Brak checked exceptions — nie trzeba deklarować ani jawnie obsługiwać.
  • Konstrukcja try może działać jako wyrażenie (zwraca rezultat).
  • Można używać finally do końcowych działań (np. zamykania zasobów), ale nie należy wykonywać operacji modyfikujących zwracane wartości.

Pytania z podstępem.

Co się dzieje, jeśli wyjątek zostanie wyrzucony w bloku finally? Jak to wpływa na zwracanie wartości?

Wyjątki wyrzucone z finally zawsze przesłaniają wszelkie inne wyjątki lub return z bloku try/catch, co może prowadzić do utraty ważnych danych o przyczynie awarii.

fun demo(): Int = try { 1 } finally { throw RuntimeException("error in finally") }

Czy try/catch może być wyrażeniem w Kotlinie? Czym różni się od Java?

Tak. try/catch/finally w Kotlinie to wyrażenia, więc mogą zwracać wynik i być używane w miejscach, gdzie dozwolone są wartości.

val value = try { risky() } catch (e: Exception) { fallback() }

W Java to tylko operatory.

Czy konieczne jest przechwytywanie wyjątków w Kotlinie, jeśli wywoływana jest zewnętrzna funkcja Java z throws?

Nie. Ponieważ checked exceptions są ignorowane przez kompilator Kotlin: można ich nie przechwytywać jawnie, ale mogą one być nadal wyrzucane w trakcie wykonywania.

Typowe błędy i antywzorce

  • Nadmierne przechwytywanie wszystkich wyjątków (catch (e: Exception)) — łapiemy to, czego nie potrafimy poprawnie obsłużyć.
  • Niepoprawne użycie finally — modyfikowanie zwracanej wartości lub przekazywanie wyjątków.
  • Próbując używać declarations throws wewnątrz kodu Kotlin.

Przykład z życia

Negatywny przypadek

Wstawiają catch (e: Exception) wszędzie, nie różnicując prawdziwych i oczekiwanych błędów. Używają finally do zwracania wartości, co prowadzi do nieoczekiwanego zachowania w przypadku błędów.

Zalety:

  • Bardzo odporny na wszelkie awarie kod

Wady:

  • Trudny do debugowania
  • Utracone komunikaty o błędach i przyczyny ich wystąpienia
  • Ukryte prawdziwe problemy

Pozytywny przypadek

Używają przechwytywania tylko oczekiwanych błędów, finally tylko do czyszczenia zasobów, a try/catch traktują jako wyrażenia tam, gdzie zwiększa to czytelność.

Zalety:

  • Łatwy do czytania, utrzymywania i testowania
  • Przejrzysta obsługa błędów

Wady:

  • Wymaga dyscypliny przy projektowaniu API i obsługi wyjątków