EMZETT.
Login

Kurz: Scala 3 hat echte Enums, auch mit Parametern (algebraische Datentypen):

Teil des Kurses Scala

Kapitel 5 von 8 im Kurs Scala (Abschnitt „Objektorientiert und funktional“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Enums

Scala 3 hat echte Enums, auch mit Parametern (algebraische Datentypen):

enum Farbe:
  case Rot, Gruen, Blau
 
enum Form:
  case Kreis(r: Double)
  case Rechteck(b: Double, h: Double)
 
def flaeche(f: Form): Double = f match
  case Form.Kreis(r) => math.Pi * r * r
  case Form.Rechteck(b, h) => b * h
 
@main def main(): Unit =
  println(Farbe.Rot)
  println(Farbe.values.toList)
  println(Farbe.valueOf("Blau").ordinal)
  println(Farbe.Gruen == Farbe.Gruen)
  println(f"${flaeche(Form.Kreis(1))}%.2f")
  println(flaeche(Form.Rechteck(2, 3)))

Ausgabe:

Rot
List(Rot, Gruen, Blau)
2
true
3.14
6.0

Sealed Traits und Exhaustivität

Bei sealed prüft der Compiler, ob match alle Fälle abdeckt:

sealed trait Ausdruck
case class Zahl(n: Int) extends Ausdruck
case class Plus(a: Ausdruck, b: Ausdruck) extends Ausdruck
case class Mal(a: Ausdruck, b: Ausdruck) extends Ausdruck
 
def eval(e: Ausdruck): Int = e match
  case Zahl(n) => n
  case Plus(a, b) => eval(a) + eval(b)
  case Mal(a, b) => eval(a) * eval(b)
 
def zeige(e: Ausdruck): String = e match
  case Zahl(n) => n.toString
  case Plus(a, b) => s"(${zeige(a)} + ${zeige(b)})"
  case Mal(a, b) => s"${zeige(a)} * ${zeige(b)}"
 
@main def main(): Unit =
  val e = Mal(Plus(Zahl(1), Zahl(2)), Zahl(4))
  println(zeige(e))
  println(eval(e))

Ausgabe:

(1 + 2) * 4
12

Option, Either und Try

Statt null und Ausnahmen nutzt Scala Werte, die Fehler beschreiben:

import scala.util.{Try, Success, Failure}
 
def teile(a: Int, b: Int): Either[String, Int] =
  if b == 0 then Left("Division durch null") else Right(a / b)
 
@main def main(): Unit =
  println(teile(10, 2))
  println(teile(1, 0))
  val r = for
    x <- teile(100, 5)
    y <- teile(x, 2)
  yield y + 1
  println(r)
  println(teile(1, 0).left.map(_.toUpperCase))
  println(teile(8, 2).fold(f => s"Fehler: $f", v => s"Wert: $v"))
 
  println(Try("42".toInt))
  println(Try("abc".toInt).isFailure)
  Try("x".toInt) match
    case Success(v) => println(v)
    case Failure(e) => println(s"Fehlgeschlagen: ${e.getClass.getSimpleName}")
  println(Try("7".toInt).map(_ * 2).getOrElse(-1))
  println(Try("q".toInt).getOrElse(-1))
  println("5".toIntOption)
  println("x".toIntOption.map(_ + 1).getOrElse(0))

Ausgabe:

Right(5)
Left(Division durch null)
Right(11)
Left(DIVISION DURCH NULL)
Wert: 4
Success(42)
true
Fehlgeschlagen: NumberFormatException
14
-1
Some(5)
0

Ausnahmen

class MeinFehler(msg: String) extends Exception(msg)
 
def pruefe(n: Int): Int =
  if n < 0 then throw MeinFehler(s"negativ: $n")
  n
 
@main def main(): Unit =
  try
    println(pruefe(3))
    println(pruefe(-2))
  catch
    case e: MeinFehler => println(s"Eigener Fehler: ${e.getMessage}")
    case e: Exception => println("anderer Fehler")
  finally println("fertig")

Ausgabe:

3
Eigener Fehler: negativ: -2
fertig

Merke

  • enum für Aufzählungen und ADTs, sealed für Exhaustivitäts-Prüfung
  • Option für Fehlendes, Either für Fehler mit Grund, Try kapselt Ausnahmen
  • for-Comprehensions verketten diese Typen
  • Ausnahmen mit try/catch/finally bleiben für Ausnahmefälle

Übungsaufgabe

Schreibe einen kleinen Rechner als sealed trait mit Either-Fehlern.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Klassen, Traits und Case Classes

Weiter: Generics, Given und Typklassen

Alle Kapitel: Scala im Überblick