Fehlerbehandlung in Kotlin: try/catch und fortgeschrittene Techniken

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Fortgeschrittene Fehlerbehandlung in Kotlin: von try/catch bis zu funktionalen Ansätzen



Die Fehlerbehandlung ist eine der zentralen Aufgaben bei der Entwicklung zuverlässiger Software. In Kotlin gibt es neben dem klassischen Mechanismus try/catch/finally eine ganze Reihe fortschrittlicher Werkzeuge: Result, runCatching, sealed class und funktionale Monaden. In diesem Artikel werden wir uns ansehen, wie Sie über die grundlegende Nutzung hinausgehen und Ihren Code sicherer, lesbarer und vorhersagbarer machen können.



1. Klassisches try/catch: Wann ist es noch sinnvoll?



Das grundlegende try/catch in Kotlin ist ein Ausdruck, der einen Wert zurückgeben kann. Das ist der erste Unterschied zu Java.



val result: String = try {    riskyOperation()} catch (e: IOException) {    "E/A-Fehler: ${e.message}"} catch (e: Exception) {    "Unbekannter Fehler: ${e.message}"} finally {    println("Der finally-Block wird immer ausgeführt")}


Trotz der Einfachheit hat dieser Ansatz Nachteile:

  • Ausnahmen sind eine teure Operation (Erstellung des Stacktrace).
  • Der Fehlertyp wird auf Typebene nicht festgehalten – die Behandlung kann leicht übersehen werden.
  • Der Code wird bei vielen verschachtelten Operationen unübersichtlich.


Wann sollte man try/catch verwenden?

  • Bei der Arbeit mit externen Ressourcen (Dateien, Netzwerk, Datenbanken).
  • In Bibliothekscode, wo Ausnahmen Teil des Vertrags sind.
  • Für kritische Fehler, die nicht lokal behandelt werden können.


2. Funktionaler Ansatz: Result und runCatching



Kotlin bietet die eingebaute Klasse Result, die das Ergebnis einer Operation darstellt, die entweder erfolgreich sein oder einen Fehler liefern kann. Dies ist eine Alternative zu Ausnahmen, die keinen teuren Stacktrace erfordert.



fun divide(a: Int, b: Int): Result<Int> {    return runCatching {        a / b    }}

fun main() { val result = divide(10, 0) result.onSuccess { value -> println("Ergebnis: $value") }.onFailure { error -> println("Fehler: ${error.message}") }}


Methoden von Result

  • getOrNull() – gibt den Wert oder null zurück.
  • getOrDefault(default) – gibt den Wert oder einen Standardwert zurück.
  • map und flatMap – für Transformationsketten.
  • recover – Wiederherstellung nach einem Fehler.


val safeResult = runCatching { riskyOperation() }    .map { it.toUpperCase() }    .recover { "default_value" }    .getOrDefault("fallback")


Wichtig: Result ist nicht für die Übergabe zwischen Threads oder die Speicherung in Klassenfeldern gedacht – verwenden Sie es nur als lokalen Wert.



3. Sealed Klassen: Fehlermodellierung auf Typebene



Wenn alle möglichen Zustände (Erfolg, verschiedene Fehlertypen) klar definiert werden müssen, helfen sealed class. Sie sind ein mächtiges Werkzeug zur Erstellung algebraischer Datentypen.



sealed class NetworkResult<out T> {    data class Success<T>(val data: T) : NetworkResult<T>()    data class Error(val message: String, val code: Int) : NetworkResult<Nothing>()    object Loading : NetworkResult<Nothing>()}

fun fetchUser(id: Int): NetworkResult<User> { return try { val user = api.getUser(id) NetworkResult.Success(user) } catch (e: HttpException) { NetworkResult.Error("Server hat einen Fehler zurückgegeben", e.code()) } catch (e: Exception) { NetworkResult.Error("Unbekannter Fehler", 0) }}

fun handleResult(result: NetworkResult<User>) { when (result) { is NetworkResult.Success -> println("Benutzer: ${result.data.name}") is NetworkResult.Er

Blogs

Buchempfehlungen