Von Java zu Scala: Der Paradigmenwechsel in der JVM-Welt

Funktional und objektorientiert auf derselben Plattform: was Scala 3 kann, was Java übernommen hat und wo der Unterschied bleibt
Aktualisiert:
Abstract
- #Scala
- #Java
- #JVM
- #Programmiersprachen
- #Funktional
- #Objektorientiert
- #Scala 3
- #Pattern Matching
Java hat Lambdas, Records und Pattern Matching nachgerüstet. Wann lohnt sich Scala trotzdem noch?
Vor fünfzehn Jahren war der Abstand zwischen Java und Scala groß. Java kannte keine Lambdas, keine unveränderlichen Datenklassen und kein Pattern Matching. Scala hatte all das, und wer auf der Java Virtual Machine (JVM) funktional programmieren wollte, landete fast zwangsläufig dort. Heute sieht das anders aus: Java hat viele dieser Ideen übernommen, oft mit sichtbarem Vorbild Scala.
Damit stellt sich die Frage neu. Ist Scala noch ein Paradigmenwechsel oder nur eine Geschmacksfrage? Die kurze Antwort: Der Unterschied ist kleiner geworden, aber nicht verschwunden. Scala denkt Programme von Grund auf als Ausdrücke und unveränderliche Werte, Java bleibt eine objektorientierte Sprache mit funktionalen Ergänzungen. Für welche Teams das den Ausschlag gibt, zeigt dieser Überblick.
Was Scala ausmacht
Scala steht für „scalable language“. Die Sprache entstand an der École polytechnique fédérale de Lausanne (EPFL) unter Leitung des Informatikers Martin Odersky, die erste öffentliche Version erschien 2004. Odersky hatte zuvor an den Generics für Java und am Java-Compiler mitgearbeitet. Er kannte die Grenzen von Java also aus nächster Nähe.
Die Grundidee: Objektorientierung und funktionale Programmierung sind keine Gegensätze. In Scala ist jeder Wert ein Objekt, und jede Funktion ist ein Wert. Klassen, Vererbung und Interfaces (in Scala „Traits“) gibt es genauso wie Funktionen höherer Ordnung, also Funktionen, die andere Funktionen als Parameter erhalten oder zurückgeben.
Seit 2021 ist Scala 3 die aktuelle Sprachgeneration. Sie brachte eine überarbeitete, auf Wunsch einrückungsbasierte Syntax, echte Aufzählungstypen mit enum, ein klareres Modell für implizite Parameter (given und using) und Erweiterungsmethoden. Für Unternehmen gibt es eine Linie mit Langzeitunterstützung: seit September 2026 Scala 3.9 LTS, die Vorgängerlinie Scala 3.3 LTS wird noch bis September 2027 gepflegt. Scala 2.13 wird weiter gepflegt, weil große Codebasen und einige Bibliotheken noch darauf laufen.
Dieselbe Plattform, unterschiedliche Denkweise
Scala übersetzt in Bytecode, also in das Zwischenformat, das die JVM ausführt. Für den Betrieb heißt das: gleiche Laufzeitumgebung, gleiche Garbage Collectors, gleiche Monitoring-Werkzeuge. Ein Scala-Dienst lässt sich im selben Container-Image und mit denselben Betriebsprozessen ausrollen wie ein Java-Dienst.
Alles ist ein Ausdruck
Der wichtigste Unterschied steckt im Kleinen. In Java sind if, try oder Schleifen Anweisungen: Sie tun etwas, liefern aber keinen Wert. In Scala liefert fast jedes Konstrukt einen Wert zurück. Das erspart Hilfsvariablen, die erst deklariert und später befüllt werden:
val stufe = if umsatz > 10_000 then "Premium" else "Standard"
val zahl = try "42".toInt catch case _: NumberFormatException => 0
Java hat sich dem angenähert. Seit Java 14 ist switch als Ausdruck verfügbar und kann einen Wert zurückgeben. Für if und try gilt das bis heute nicht.
Unveränderlich als Normalfall
Scala unterscheidet zwischen val (nicht neu zuweisbar) und var (veränderlich). Die Standardbibliothek setzt auf unveränderliche Collections: Eine List oder Map wird nicht verändert, sondern jede Operation liefert eine neue Sammlung. Das klingt teuer, ist aber durch geteilte interne Strukturen effizient umgesetzt. Der Vorteil zeigt sich vor allem bei Nebenläufigkeit: Was sich nicht ändern kann, muss nicht gegen gleichzeitige Zugriffe geschützt werden.
In Java ist Unveränderlichkeit möglich, aber Handarbeit. List.of() liefert eine unveränderliche Liste, final verhindert Neuzuweisung, und Records sind unveränderlich. Die meisten Bibliotheken und viele Teams arbeiten aber weiterhin mit veränderlichen Objekten.
Was Java von Scala übernommen hat
Ein Vergleich aus dem Jahr 2014 wäre heute unfair. Java hat seither mehrere Sprachmittel ergänzt, die Scala-Entwickler:innen vertraut vorkommen:
- Lambdas und Streams seit Java 8 (2014): Funktionen als Werte und Verarbeitungsketten wie
mapundfilter. Mehr dazu im Beitrag über Lambda Expressions in Java. - Records seit Java 16: kompakte, unveränderliche Datenklassen, vergleichbar mit Scalas
case class. - Sealed Interfaces seit Java 17: Der Compiler kennt alle Untertypen einer Hierarchie.
- Pattern Matching für
switchund Record Patterns seit Java 21: Datenstrukturen direkt imswitchzerlegen.
Wie nah sich beide Sprachen inzwischen sind, zeigt ein klassisches Beispiel: Flächen für verschiedene geometrische Formen berechnen. In Java 21:
import java.util.List;
public class Formen {
sealed interface Form permits Kreis, Rechteck {}
record Kreis(double radius) implements Form {}
record Rechteck(double breite, double hoehe) implements Form {}
static double flaeche(Form form) {
// Der Compiler prüft, dass alle Formen abgedeckt sind
return switch (form) {
case Kreis(double r) -> Math.PI * r * r;
case Rechteck(double b, double h) -> b * h;
};
}
public static void main(String[] args) {
List<Form> formen = List.of(new Kreis(1.0), new Rechteck(2.0, 3.0));
formen.forEach(f -> System.out.printf("%.2f%n", flaeche(f)));
}
}
Dasselbe in Scala 3:
enum Form:
case Kreis(radius: Double)
case Rechteck(breite: Double, hoehe: Double)
def flaeche(form: Form): Double = form match
case Form.Kreis(r) => math.Pi * r * r
case Form.Rechteck(b, h) => b * h
@main def start(): Unit =
val formen = List(Form.Kreis(1.0), Form.Rechteck(2.0, 3.0))
formen.foreach(f => println(f"${flaeche(f)}%.2f"))
Die Scala-Fassung ist kürzer, die Logik aber dieselbe. Fehlt ein Fall, meldet der Java-Compiler einen Fehler, der Scala-Compiler eine Warnung. Der Abstand bei der Syntax ist also deutlich geschrumpft. Was die Neuerungen von Java 21 im Einzelnen bringen, beschreibt unser Überblick über Java 21.
Wo Scala weiterhin vorn liegt
Hinter der ähnlichen Oberfläche gibt es Unterschiede, die in größeren Codebasen spürbar werden.
Option statt null
Java kennt Optional, verwendet es aber vor allem für Rückgabewerte. Felder und Parameter sind weiterhin oft null. In Scala ist Option der übliche Weg, fehlende Werte auszudrücken, und die Standardbibliothek ist darauf ausgelegt. Angenommen, ein Webshop sucht die Lieferadresse eines Kunden, die nicht immer hinterlegt ist:
case class Kunde(name: String, plz: Option[String])
def versandzone(kunde: Kunde): String =
kunde.plz
.map(_.take(1))
.map(ziffer => s"Zone $ziffer")
.getOrElse("Adresse fehlt")
Eine NullPointerException ist hier ausgeschlossen, solange die Daten nicht aus Java-Code kommen. Dort liegt die Grenze: Java-Bibliotheken können weiterhin null liefern, und Scala-Code muss solche Werte an der Schnittstelle abfangen, etwa mit Option(wert).
Ein ausdrucksstärkeres Typsystem
Scala erlaubt Konstrukte, die Java nicht kennt: Union-Typen (String | Int), Typklassen über given, höherstufige Typen für Bibliotheken wie Cats oder ZIO. Das ermöglicht sehr allgemeine, wiederverwendbare Abstraktionen. Gleichzeitig ist es der Grund für den Ruf der Sprache, schwer lesbar zu sein. In schlecht geführten Projekten entsteht Code, den nur die ursprünglichen Autor:innen verstehen.
Erweiterungsmethoden und Komposition
Mit Erweiterungsmethoden lassen sich bestehenden Typen neue Methoden hinzufügen, ohne sie zu verändern. Das hilft bei gut lesbaren, fachlichen Schnittstellen:
extension (betrag: BigDecimal)
def mitMwst: BigDecimal = betrag * BigDecimal("1.19")
val brutto = BigDecimal("100.00").mitMwst
Java hat hierfür kein Gegenstück und setzt weiterhin auf statische Hilfsmethoden.
Zusammenspiel mit Java in der Praxis
Scala kann jede Java-Bibliothek direkt verwenden. JDBC-Treiber, Logging, java.time oder Kafka-Clients funktionieren ohne Adapter. Kleine Reibungspunkte gibt es bei Collections: Java-Listen sind veränderlich, Scala-Listen nicht. Für die Umwandlung gibt es Konverter:
import scala.jdk.CollectionConverters.*
val javaListe: java.util.List[String] = java.util.List.of("Köln", "Bonn")
val scalaListe: List[String] = javaListe.asScala.toList
Umgekehrt ist Scala-Code aus Java heraus nutzbar, aber weniger angenehm. Scala-spezifische Konstrukte wie Typklassen, Default-Parameter oder Companion Objects sehen aus Java-Sicht ungewohnt aus. In gemischten Projekten bewährt sich deshalb eine klare Grenze: Scala-Module bieten nach außen eine schlichte, Java-freundliche Schnittstelle an.
Ein Team kann also schrittweise einsteigen, etwa mit einem neuen Microservice oder einem Datenverarbeitungsjob, ohne die bestehende Java-Anwendung anzufassen. Eine Migration von Java zu Scala lohnt sich dagegen selten als Selbstzweck.
Werkzeuge und Ökosystem
Das klassische Build-Werkzeug ist sbt. Eine Minimalkonfiguration für ein Scala-3-Projekt sieht so aus:
// build.sbt
ThisBuild / scalaVersion := "3.9.0"
lazy val root = project
.in(file("."))
.settings(
name := "bestellservice",
libraryDependencies += "org.scalameta" %% "munit" % "1.3.6" % Test
)
Daneben gibt es Scala CLI für Skripte und kleine Projekte, das inzwischen als Standardbefehl scala ausgeliefert wird. Maven und Gradle unterstützen Scala über Plugins, was in Java-Organisationen den Einstieg erleichtert. Als Entwicklungsumgebung dienen IntelliJ IDEA mit Scala-Plugin oder Visual Studio Code mit der Erweiterung Metals.
Bei den Bibliotheken hat sich einiges verschoben:
- Apache Spark ist in Scala geschrieben und der bekannteste Grund, warum Datenteams mit Scala in Berührung kommen. Spark setzt allerdings weiterhin auf Scala 2.13, nicht auf Scala 3. Viele Spark-Nutzer arbeiten zudem mit Python über PySpark.
- Akka, lange das Standard-Framework für nebenläufige Systeme nach dem Aktorenmodell, steht seit 2022 unter der Business Source License (BSL), die für den Produktivbetrieb in der Regel eine kostenpflichtige Lizenz verlangt. Als freie Fortführung entstand Apache Pekko unter dem Dach der Apache Software Foundation.
- Funktionale Effekt-Bibliotheken wie Cats Effect und ZIO prägen das moderne Scala-Backend. Sie sind mächtig, aber ein eigenes Lernfeld.
- Für Webanwendungen gibt es unter anderem das Play Framework und schlanke HTTP-Bibliotheken wie http4s oder Tapir.
Zu den frühen Großanwendern gehörte Twitter: Das Unternehmen ersetzte ab Ende der 2000er-Jahre erste Backend-Dienste in Ruby durch Scala-Anwendungen auf der JVM.
Scala, Kotlin oder modernes Java?
Wer heute eine Alternative zu Java auf der JVM sucht, landet meist bei Kotlin. Kotlin ist näher an Java, leichter zu lernen und durch Android sowie Spring breit unterstützt. Den direkten Vergleich zwischen diesen beiden Sprachen zeigt der Beitrag Kotlin vs. Java.
Scala besetzt eine andere Nische. Die Sprache ist für Teams interessant, die konsequent funktional arbeiten wollen: mit unveränderlichen Daten, Effekt-Systemen und einem Typsystem, das viele Fehler schon beim Kompilieren ausschließt. Typische Einsatzfelder sind Datenpipelines, Finanzanwendungen mit komplexen Regeln und Backend-Dienste mit hohen Anforderungen an Korrektheit.
Eine grobe Orientierung:
| Situation | Naheliegende Wahl |
|---|---|
| Bestehendes Java-Team, klassische Geschäftsanwendung | Modernes Java |
| Wunsch nach knapperem Code und Null-Sicherheit | Kotlin |
| Konsequent funktionale Architektur, starke Typisierung | Scala 3 |
| Datenverarbeitung mit Spark auf der JVM | Scala 2.13 |
Was das für Ihr Team bedeutet
Für die meisten Java-Teams ist ein Umstieg auf Scala heute kein Muss. Records, Sealed Interfaces und Pattern Matching haben die größten Lücken geschlossen, und der Abstand bei Lesbarkeit und Kürze ist überschaubar. Wer Java 21 oder neuer einsetzt, bekommt einen Großteil der Ausdruckskraft, die früher für Scala sprach.
Scala lohnt sich dort, wo die funktionale Denkweise selbst das Ziel ist. Dann bringt die Sprache echte Vorteile: weniger Seiteneffekte, besser testbare Logik, ein Typsystem als Sicherheitsnetz. Der Preis ist ein kleinerer Arbeitsmarkt, eine steilere Einarbeitung und ein Ökosystem, in dem der Übergang von Scala 2 zu Scala 3 noch nicht überall abgeschlossen ist.
Unsere Empfehlung: Prüfen Sie zuerst, ob modernes Java Ihr Problem bereits löst. Wenn ein Team ernsthaft mit Scala liebäugelt, eignet sich ein abgegrenztes Pilotprojekt, etwa ein Datenverarbeitungsjob, besser als die Umstellung einer Kernanwendung. Bei der Auswahl von Sprache und Architektur unterstützt Sie unsere Beratung zur Softwareentwicklung.
Häufige Fragen zu Scala und Java
Ist Scala schwerer zu lernen als Java?
Die Grundlagen sind für Java-Entwickler:innen schnell erlernt, denn Klassen, Methoden und Bibliotheken funktionieren ähnlich. Schwieriger wird es bei fortgeschrittener funktionaler Programmierung mit Typklassen und Effekt-Bibliotheken. Wie steil die Lernkurve ist, hängt stark vom Stil ab, den ein Team wählt.
Läuft Scala-Code so schnell wie Java-Code?
Beide laufen auf derselben JVM mit demselben Just-in-Time-Compiler. Im Normalfall gibt es daher keinen grundsätzlichen Unterschied. Unveränderliche Collections und viele kleine Objekte können in Einzelfällen mehr Speicher kosten, lassen sich aber gezielt optimieren.
Kann man Scala 2 und Scala 3 mischen?
Teilweise. Scala 3 kann viele Bibliotheken nutzen, die für Scala 2.13 kompiliert wurden. Umgekehrt liest Scala 2.13 mit der Option -Ytasty-reader Scala-3-Bibliotheken nur bis Version 3.7, nicht ab Scala 3.8. Makros aus Scala 2 funktionieren in Scala 3 nicht, was einige ältere Bibliotheken betrifft. Eine Migration lässt sich deshalb meist modulweise planen.
Eignet sich Scala für Webentwicklung?
Ja, mit Frameworks wie Play oder Bibliotheken wie http4s und Tapir. Mit Scala.js lässt sich Scala-Code zudem nach JavaScript übersetzen. In der Breite ist das Ökosystem für Webanwendungen aber kleiner als bei Java mit Spring oder Kotlin mit Ktor.
Kategorien
Schlagworte
- #Scala
- #Java