1. Was ist eine Exception?

[THEORIE]

Manche Dinge gehen in einem Programm auf eine Weise schief, die der normale Kontrollfluss nicht abbilden kann: ein Nutzer schickt eine ungültige Bestellmenge, eine Datei fehlt, ein Netzwerkaufruf bricht ab. Für genau solche Fälle gibt es Exceptions — sie signalisieren, dass etwas außergewöhnlich schiefgelaufen ist, sei es durch einen Programmierfehler, durch ein externes System oder durch die Laufzeitumgebung selbst.

Eine Exception wirfst du mit throw, gefolgt von einer Instanz einer Exception-Klasse:

fun registriereBestellung(menge: Int, lagerbestand: Int) {
    if (menge > lagerbestand) {
        throw IllegalStateException("Nicht genug Lagerbestand: $menge angefragt, $lagerbestand verfügbar")
    }
    println("Bestellung ($menge Stück) erfolgreich registriert")
}

Wird eine Exception geworfen und nirgendwo gefangen, bricht das Programm ab und gibt einen Stacktrace aus — die Kette von Funktionsaufrufen, die zur Exception geführt hat, plus Exception-Typ und Nachricht. Das ist kein Bug im Tooling, sondern gewolltes Verhalten: lieber ein sichtbarer Absturz mit klarer Fehlermeldung als ein Programm, das mit einem inkonsistenten Zustand stillschweigend weiterläuft.

Alle Exception-Klassen bilden eine Hierarchie mit Throwable an der Spitze:

Throwable
├── Error            – schwerwiegende Probleme, i. d. R. nicht sinnvoll behandelbar
│   ├── OutOfMemoryError
│   └── StackOverflowError
└── Exception         – Fehler, die ein Programm potenziell behandeln kann
    └── RuntimeException
        ├── IllegalArgumentException
        ├── IllegalStateException
        ├── IndexOutOfBoundsException
        └── NumberFormatException

Error steht für Probleme, bei denen Behandeln meist wenig bringt — ist der Arbeitsspeicher voll, hilft kein catch. Exception und ihre Unterklassen sind der Bereich, in dem Fehlerbehandlung sinnvoll ist, und genau dort setzt diese Lektion an.

Wichtig ist die Abgrenzung: Exceptions sind für außergewöhnliche Fälle gedacht, nicht für jeden erwartbaren Ausgang einer Funktion. Dass ein Kunde in einer Liste nicht gefunden wird, ist meistens kein außergewöhnlicher Fehler, sondern ein ganz normaler Fall, den die Funktion einkalkulieren sollte — dafür gibt es bessere Werkzeuge, die später in diesem Kurs drankommen. Wann eine Exception die richtige Wahl ist und wann nicht, zieht sich als Frage durch den ganzen Kurs.

[VORHERSAGE]

Lies den Code genau durch, bevor du ihn ausführst.

vorhersage.kt
fun registriereBestellung(menge: Int, lagerbestand: Int) {
    if (menge > lagerbestand) {
        throw IllegalStateException("Nicht genug Lagerbestand: $menge angefragt, $lagerbestand verfügbar")
    }
    println("Bestellung ($menge Stück) erfolgreich registriert")
}

fun main() {
    println("Start")
    registriereBestellung(15, 10)
    println("Ende")
}

Was gibt das Programm aus? Wird "Ende" noch ausgegeben?

[AUFGABE]

Schreibe eine Funktion registriereBestellung(menge: Int, lagerbestand: Int). Ist menge größer als lagerbestand, wirf eine IllegalStateException mit der Nachricht "Nicht genug Lagerbestand: $menge angefragt, $lagerbestand verfügbar". Andernfalls gib "Bestellung ($menge Stück) erfolgreich registriert" aus. Rufe die Funktion in main() mit menge = 15 und lagerbestand = 10 auf — das Programm soll mit einem Stacktrace abbrechen. Catching ist Thema der nächsten Lektion, hier geht es nur um throw.

aufgabe.kt
fun registriereBestellung(menge: Int, lagerbestand: Int) { // TODO: bei zu wenig Lagerbestand eine IllegalStateException werfen // TODO: sonst Erfolgsmeldung ausgeben } fun main() { registriereBestellung(15, 10) }

Bevor du einen Hint bekommst: Was hast du schon probiert, und woran hängt es genau?

[VERSTÄNDNIS-CHECK]

1. Wofür sollte man Exceptions laut gängiger Konvention NICHT einsetzen?

2. Throwable ist die Basisklasse der Kotlin-Exception-Hierarchie. Welche zwei direkten Unterklassen hat sie?