Einstieg

Exceptions (Ausnahmen) betreffen Methoden und Funktionen gleichermaßen. Der Einfachheit wird im Folgenden nur von Methoden gesprochen.

Im Normalfall erwarten wir, dass eine Methode einen bestimmten Wert berechnet/bestimmt oder gewünschte Seiteneffekte ausführt. Jedoch ist eine Methode nicht immer in der Lage die gewünschte Funktionalität wie erwartet zu liefern.

Ein Beispiel ist, wenn eine Methode die Daten einer bestimmten Datei auswerten soll, wobei die angegebene Datei nicht existiert. Oder es soll eine Berechnung für einen Parameter durchgeführt werden, wobei der Parameter außerhalb des zulässigen Wertebereichs liegt.

Nun könnte die entsprechende Methode das laufende Programm einfach beenden, was wir eigentlich nicht wollen. Eine weitere Möglichkeit wäre es, die Methode einen codierten Wert zurückgeben zu lassen. Das würde aber in der Aufrufhirachie zu erheblichen Problemen in der Programmierung führen.

Anstatt die Methode mit einem Rückgabewert zu beenden oder bis zum Ende arbeiten zu lassen kommen hier die Exceptions (Ausnahmen) ins Spiel. Die entsprechende Methode wird nicht wie erwartet bis zum Ende ausgeführt und es wird auch kein Rückgabewert zurückgegeben, sondern es wird eine Exception geworfen.

Wirft eine Methode eine Exception, gibt es für die aufrufende Methode zwei Möglichkeiten. Die Methode kann die Exception geeignet behandeln oder an die ihrerseits aufrufende Methode weiterreichen. Gibt es keine weitere aufrufende Methode, wird das Programm abgebrochen (zumindest bei Single-Thread Anwendungen).

Ein wesentlicher Unterschied zu Java ist, dass keine Exceptions explizit weitergeleitet werden muss. Wird eine Exception nicht behandelt, wird Sie automatisch an die aufrufende Methode weitergereicht. In Scala verhalten sich alle Exceptions wie eine RuntimeException in Java. Aus diesem Grund gibt es auch keine throws Deklaration für Methoden in Scala.



Erster Kontakt

Für einen ersten Kontakt mit Exceptions in Scala erstellen Sie eine Scala Klasse mit folgendem Inhalt. Stellen Sie zudem sicher, dass es keine Datei mit dem Namen "c:\test.txt" existiert, bzw. wählen Sie einen Dateinamen einer nicht existierenden Datei.

import java.io._
         
object FirstException{
  def main(args: Array[String]){
    println("Start")
    val is = new FileInputStream("c:\\test.txt")
    println("End")
  }
}
itmapa.de - X2H V 0.18

Kompilieren Sie nun die Scala Datei. Wir stellen fest, dass die Kompilierung ohne Fehlermeldungen durchläuft, da in Scala keine Exception explizit verarbeitet bzw. weitergereicht werden muss.

Wenn Sie nun die kompilierte Scala Klasse ausführen, erhalten Sie die geworfene java.io.FileNotFoundException als Fehlerausgabe. Die Ausgabe auf der Systemausgabe lautet in etwa:

Start
Exception in thread "main" java.io.FileNotFoundException: c:\test.txt 
(Das System kann die angegebene Datei nicht finden)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.(FileInputStream.java:106)
at java.io.FileInputStream.(FileInputStream.java:66)
at firstException.FirstException$.main(FirstException.scala:8)
at firstException.FirstException.main(FirstException.scala)
...        
itmapa.de - X2H V 0.18



Eine Exception behandeln

Im Normalfall möchten wir nicht, dass eine Anwendung beendet wird, nur weil eine Exceptio aufgetreten ist. Um dies zu erreichen, muss die Exception an geeigneter Stelle im Programm behandelt werden. Erreichen tun wir dies, indem wir die Methode, welche eine Exception werfen, kann in einem try - Block definieren. Im Anschluss folgt ein catch - Block, indem eine geworfene Exception behandelt werden kann.

Das nachfolgende Beispiel HandleException zeigt grundlegend diese Vorgehensweise.

import java.io._
         
object HandledException {
  def main(args: Array[String]){
    try {
      val is = new FileInputStream("c:\\test.txt")
    }
    catch{
      case fnfe : FileNotFoundException => 
                 println("FileNotFoundException catched")
    }
  }
}
itmapa.de - X2H V 0.18



Mehrere Exception behandeln

Möchten wir mehrere Exception nach einem try-Block behandeln geschieht das in Scala in einem catch-Block. Die unterschiedliche Behandlung unterschiedlicher Exception erreichen wir über die Anwendung des Pattern Matchings. Im nachfolgenden Beispiel erreichen wir unterschiedliche Ausgaben auf der Systemausgabe je nach aufgetretenen Typ der Exception. Sollte in den case Anweisungen kein passender Exception Typ gefunden werden wird die Exception über den Platzhalter "_" gefangen und es erfolgt die Ausgabe, dass ein unbekannter Fehler aufgetreten ist.

import java.io.FileNotFoundException
          
object MoreThanOneException {
  def main(args: Array[String]) {
    try {
      throw new IllegalArgumentException
    }
    catch {
      case fnfe : FileNotFoundException => println("FileNotFoundExcepton catched")
      case npe : NullPointerException => println("NullPointerException catched")
      case iae : IllegalArgumentException => println("IllegalArgumentException catched")
      case _ => println("Unknwon error raised")
    }
  }
}
itmapa.de - X2H V 0.18



Aufräumarbeiten - finally

Schließen wir einen Quelltextteil, in dem eine Exception geworfen werden kann, in einem try-Block ein, können wir mit der finally-Klausel Aktionen definieren, die in jedem Fall ausgeführt werden. Die Anweisung bzw. der Anweisungsblock, welcher der finally-Klausel zugeordnet ist, wird ausgeführt, egal ob eine Exception geworfen wird oder nicht.

Die Verwendung der finally-Klausel bietet sich besonders an, um z.B. offene Dateien zu schließen oder Datenbankverbindungen zu trennen.

import java.io.FileNotFoundException
          
object MyFinally {
  def main(args: Array[String]) {
    println("Start")
    try {
      println(compute(3.0,2.0))
      println(compute(4.0,0.0))
      println(compute(5.0,2.0))
    }   
    finally { 
      println("cleanup efforts")
    }
    println("End")
  }
  
  def compute(denumerator: Double, numerator: Double) : Double = {
    require(numerator != 0.)
    denumerator / numerator
  }
}
itmapa.de - X2H V 0.18

Der Ablauf des Programms führt zu folgender Ausgabe auf der Systemausgabe:

Start
1.5
cleanup efforts
Exception in thread "main" java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:145)
at MyFinally$.compute(MyFinally.scala:18)
at MyFinally$.main(MyFinally.scala:8)
at MyFinally.main(MyFinally.scala)