scalatutorial.de

Stufe 3Objekte und Typen · Lektion 18 von 34

Exception Handling

Aktualisiert am 24. September 20263 Min. LesezeitSchritt 6/6 dieser Stufe

Scala übernimmt das Ausnahmesystem der JVM, schreibt es aber knapper: catch arbeitet mit Pattern Matching, try ist ein Ausdruck, und es gibt keine geprüften Ausnahmen. Trotzdem setzt idiomatischer Scala-Code Exceptions sparsam ein.

Ausnahmen werfen

 Scala 3
def wurzel(x: Double): Double =
  if x < 0 then throw IllegalArgumentException(s"negativ: $x")
  else math.sqrt(x)

throw ist ein Ausdruck vom Typ Nothing. Weil Nothing ein Untertyp jedes anderen Typs ist, passt er in jeden Zweig – der Rückgabetyp von wurzel bleibt Double. Ausnahmen sind gewöhnliche Java-Klassen; Sie können die vorhandenen nutzen oder eigene von Exception ableiten.

Ausnahmen fangen

 Scala 3
import java.io.{FileNotFoundException, IOException}

def lies(pfad: String): String =
  try
    scala.io.Source.fromFile(pfad).mkString
  catch
    case e: FileNotFoundException => s"Datei fehlt: ${e.getMessage}"
    case e: IOException           => s"Lesefehler: ${e.getMessage}"
  finally
    println("Leseversuch beendet")

Der catch-Block ist eine Folge von case-Fällen wie beim Pattern Matching. Die Fälle werden der Reihe nach geprüft; der speziellere gehört deshalb nach oben. finally läuft immer – egal ob ein Fehler auftrat oder nicht – und eignet sich für Aufräumarbeiten. Sein Wert wird verworfen.

try ist ein Ausdruck

Wie if und match liefert auch try einen Wert:

 Scala 3
val port =
  try sys.env("PORT").toInt
  catch case _: NoSuchElementException | _: NumberFormatException => 8080

Mehrere Ausnahmetypen lassen sich mit | zusammenfassen.

Keine geprüften Ausnahmen

Java unterscheidet zwischen geprüften Ausnahmen (checked exceptions), die in der Methodensignatur stehen müssen, und ungeprüften. Scala kennt diese Unterscheidung nicht: Keine Ausnahme muss deklariert werden. Das spart Schreibarbeit, hat aber einen Preis – am Typ einer Methode sieht man nicht, ob sie scheitern kann. Wer das dokumentieren möchte, kann die Annotation @throws[IOException] verwenden; sie ist vor allem für Aufrufer aus Java gedacht.

Nicht alles fangen

Ein case _: Throwable oder case e => … fängt wirklich alles, auch schwere Fehler wie OutOfMemoryError oder Unterbrechungen von Threads. Verwenden Sie stattdessen NonFatal:

 Scala 3
import scala.util.control.NonFatal

try riskant()
catch case NonFatal(e) => println(s"Fehler: ${e.getMessage}")

NonFatal passt auf alle „normalen“ Ausnahmen und lässt Fehler durch, bei denen ein Weiterlaufen ohnehin sinnlos wäre.

Ressourcen sicher schließen: Using

Dateien, Datenbankverbindungen und Netzwerk-Sockets müssen geschlossen werden – auch im Fehlerfall. Statt verschachtelter try/finally-Blöcke gibt es scala.util.Using:

 Scala 3
import scala.util.Using
import scala.io.Source

val zeilen: scala.util.Try[Int] =
  Using(Source.fromFile("daten.csv")) { quelle =>
    quelle.getLines().count(_.nonEmpty)
  }

Using schließt die Ressource automatisch und liefert das Ergebnis als Try: Success(anzahl) oder Failure(ausnahme). Für mehrere Ressourcen gibt es Using.Manager. Das entspricht dem try-with-resources aus Java.

Eigene Ausnahmen

 Scala 3
final class UngueltigeBestellung(nachricht: String) extends Exception(nachricht)

def pruefe(menge: Int): Unit =
  if menge <= 0 then throw UngueltigeBestellung(s"Menge $menge ist nicht erlaubt")

Wann Exceptions – und wann nicht?

In funktionalem Scala-Code gelten Exceptions als Mittel für unerwartete Fehler: Programmierfehler, kaputte Konfiguration, ausgefallene Infrastruktur. Für erwartbare Fälle – ein Benutzer gibt keine Zahl ein, ein Datensatz existiert nicht – nutzt man lieber Werte, die den Fehlerfall im Typ sichtbar machen:

SituationWerkzeug
Wert kann fehlenOption
Aufruf kann mit Exception scheiternTry
Fehler mit eigener BeschreibungEither
wirklich unerwarteter FehlerException

Der Vorteil: Wer eine Methode mit dem Rückgabetyp Either[Fehler, Kunde] aufruft, muss sich um beide Fälle kümmern – der Compiler erinnert daran. Eine Exception dagegen fliegt still durch alle Schichten, bis jemand sie fängt, oder bringt das Programm zum Absturz.

Passt dazu