Das Wissensportal für IT-Professionals. Entdecke die Tiefe und Breite unseres IT-Contents in exklusiven Themenchannels und Magazinmarken.

SIGS DATACOM GmbH

Lindlaustraße 2c, 53842 Troisdorf

Tel: +49 (0)2241/2341-100

kundenservice@sigs-datacom.de

Ein Scala 3-Crashkurs

Wer nach alternativen JVM-Sprachen sucht, stößt früher oder später auch auf Scala. Diese Programmiersprache bietet einige Vorteile, wenn es um die Entwicklung von Anwendungen geht. Der Artikel möchte deshalb Highlights der Sprache in einer kleinen Sightseeing-Tour vorstellen.
Author Image
Michael Stal

Chefredakteur von JavaSPEKTRUM


  • 23.09.2022
  • Lesezeit: 24 Minuten
  • 26 Views

Motivation

Die Programmiersprache Scala existiert seit rund zwei Jahrzehnten und integriert mehrere Paradigmen wie objektorientiertes, funktionales und generisches Programmieren. Ihr Name leitet sich von „Scalable Language“ ab, denn sie bietet einen Baukasten von Idiomen und Konstrukten, mit deren Hilfe sich große, leistungsfähige Anwendungen erstellen lassen. Ein großer Vorteil von Scala ist die Lauffähigkeit auf der JVM. Daher ist Scala ein idealer Kandidat, wenn es um Programmiersprachen mit JVM-Unterstützung außerhalb von Java geht.

Neben der Lauffähigkeit auf der JVM lassen sich Scala-Programme auch nativ in Maschinencode übersetzen oder in JavaScript. Die Sprache zeichnet sich durch ein statisches Typsystem aus, benutzt aber Typ-Inferenz, um Datentypen aus dem Kontext herzuleiten. Das verleiht der Sprache ein wenig die Würze dynamischer Sprachen. Andere Sprachen wie C#, Kotlin und Java haben hier zwar längst nachgezogen, wahrscheinlich aber in Anlehnung an Scala. Zudem können Entwickler Scala einsetzen, um Domain Specific Languages (DSL) zu implementieren, etwa zum Bereitstellen idiomatischer Sprachkonstrukte.

Ein großer Vorteil ist, dass der Sprachschöpfer Martin Odersky mit seinem Team an der School of Computer and Communication Sciences at the École Polytechnique Fédérale de Lausanne (EPFL) Scala ständig weiterentwickelt und verbessert. Seit rund zwei Jahren gibt es nun Scala 3.x, das eine ganze Reihe von signifikanten Änderungen gegenüber den Vorgängerversionen beinhaltet.

Tools

Das Programmieren von Scala-Anwendungen ist auf verschiedene Arten möglich. Etwa mit einer IDE wie zum Beispiel IntelliJ oder Visual Studio Code, mittels der sbt-Werkzeugkette (sbt = Scala Build Tool => https://www.scala-sbt.org/download.html) oder mit der Online-Umgebung scastie (https://scastie.scala-lang.org/). Letztere eignet sich gut für das unproblematische Kennenlernen von Scala, ohne extra eigene Software installieren zu müssen, auch wenn Nutzer dafür ein paar Einschränkungen hinnehmen müssen. Auf die Anweisung zur Installation einer lauffähigen Scala-Umgebung sei hier verzichtet. Hierfür finden sich genügend Informationen im Internet, zum Beispiel auf https://www.scala-lang.org/download.

Es wäre müßig, jedes einzelne Sprachmerkmal oder jedes Detail eingehend besprechen zu wollen. Das würde ein großes Buch füllen – geeignete Bücher finden Sie in dieser Ausgabe an anderer Stelle. Daher soll es hier nur um einige Konstrukte gehen, die charakteristisch für die Sprache sind.

Klassen und Traits

Im Gegensatz zu Java unterstützt Scala Traits, die als Mix-Ins für Klassen, Objekte oder andere Traits dienen. Am nächsten kommen sie wohl Java-Schnittstellen, erlauben aber im Gegensatz dazu sowohl abstrakte Methoden und Felder als auch implementierte Methoden.

Als Beispiel nehmen wir hier das theoretische Trait Printable, mit dem man Klassen ausstatten könnte, die eine Methode printall() implementieren sollen. Printable besteht nur aus der parameterlosen und abstrakten Methode printall() , die nichts zurückliefert (siehe Rückgabetyp Unit ):

trait Printable:
  def printall: Unit

Die Klasse Book implementiert Printable und muss dementsprechend die Methode printall() (-> override ) überschreiben:

class Book(val title:String, val isbn:String) extends Printable:
// Zweiter Konstruktor:
  def this(title:String) = this(title, "Unknown isbn")
// Methode aus Trait Printable
override def printall : Unit =
println(s"Book title = $title, ISBN = $isbn")

Würde sie printall() nicht implementieren, wäre Book eine abstrakte und damit nicht instanziierbare Klasse. Sie müsste daher mit dem Schlüsselwort abstract deklariert sein wie in:

abstract class Book(val title:String, val isbn:String) extends Printable

Während Klassen mehrere Traits implementieren können, können sie wie in Java nur eine Elternklasse besitzen.

Konstruktoren in Scala leitet der Übersetzer aus dem class-Header ab. Im obigen Fall muss es einen Konstruktor für Book geben, der zwei Zeichenketten title und isbn erwartet. Die wegen val nicht veränderbaren gleichnamigen Felder, die zugehörigen Zugriffsfunktionen sowie den genannten Konstruktor erzeugt der Compiler automatisch. Wer weitere Konstruktoren hinzufügen möchte, gibt zusätzliche Methoden mit dem Namen this an (siehe Beispiel).

Der Parameter s"Book title = $title, ISBN = $isbn" von println() in der Methode printall() repräsentiert ein Beispiel für String-Interpolation. Alle mit *$* getaggten Werte sind Variablen oder Ausdrücke, deren Inhalt zur Laufzeit in die resultierende Zeichenkette eingesetzt wird. Zeichenketten mit String-Interpolation beginnen mit dem Präfix ‚s‘.

Im folgenden Anwendungsbeispiel kreieren wir zwei Instanzen von Book. Im Gegensatz zu Scala 2 ist es nicht mehr nötig, den new-Operator zu verwenden. Erlaubt wäre es aber trotzdem wie zum Beispiel: b1 = new Book(„Bible“):

var b1 = Book("Bible") // ohne new
val b2 = Book("Programming Scala", "1234") // ohne new
b1.printall
b2.printall

->
Book title = Bible, ISBN = Unknown isbn
Book title = Programming Scala, ISBN = 6666

Wie aus den Beispielen ersichtlich, benötigt Scala keine lästigen Anführungsstriche außer für mehrere Anweisungen auf einer Zeile. Überhaupt ist Scala mit den Programmierern gnädig, können sie doch auch auf runde oder geschweifte Klammern verzichten, wenn der Compiler aus dem Kontext erkennt, was gemeint ist. Ohne geschweifte Klammern müssen Entwickler Definitionen und Deklarationen einrücken. Python-Programmierer dürften sich schnell zu Hause fühlen.

Generische Datentypen in Scala sind parametrisierbare Datentypen, die ein oder mehrere Typparameter enthalten. Als Beispiel soll eine Klasse Entity dienen, die zum einen einen Typparameter S für den Inhalt erwartet und zum anderen einen Typ T für die eindeutige Identifizierung eines Entities. Bei entity1 ist explizit der konkrete Datentyp Entity[String, BigInt] angegeben, bei entity2 leitet der Compiler den Typ durch Typreferenz ab:

class Entity[S,T](data : S, id : T):
 def stringRep() : String = s"($data , $id)"

val entity1 : Entity[String, BigInt] = Entity("Hallo", 1)
println(entity1.stringRep())

val entity2 = Entity(List("Alpha", "Beta"), 2)
println(entity2.stringRep())

->
    (Hallo , 1)
   (List(Alpha, Beta) , 2)

Kleine Randbemerkung: In Scala ist es nicht unbedingt erforderlich, Funktionsergebnisse über eine return-Anweisung zurückzuliefern. Stattdessen liefert Scala in einer Funktion wie stringRep() einfach den letzten Ausdruck als Wert zurück. Im obigen Fall ist das die Zeichenkette s"($data , $id)".

Auch Funktionen beziehungsweise Methoden lassen sich parametrisieren. Bei parametrisierbaren Funktionen kann der Compiler ebenfalls per Typinferenz den richtigen Datentyp herleiten (siehe zweiter Aufruf von fun):

def fun[S](x : S) = println(x)

fun[BigInt](42)
fun("JavaSPEKTRUM") // hier schlägt Typinferenz zu

->
42
JAVASPEKTRUM

In Scala existieren ebenso Pakete (wie in Java), um zusammengehörige Vereinbarungen zu ordnen. Ihre Aufschreibung erfolgt durch das Schlüsselwort package :

package de.javaspektrum.helper

Pakete

Das Ganze funktioniert allerdings nicht in der REPL-Umgebung, etwa in Scastie. Auch der Import von Paketen geschieht ähnlich wie in Java.
Scala-Programme, die auf der JVM laufen, können natürlich neben Scala-Paketen auch Java-Bibliotheken des JDK nutzen, was überhaupt erst den Vorteil von Scala auf der JVM ausmacht. Hier ein simples Beispiel:

import java.util.Date val date = Date() println(date)
->
Fri Aug 05 22:17:59 GMT 2022

Dadurch erst lassen sich Java-Bibliotheken wie JavaFX auch in Scala nutzen. Da allerdings einige Java-Bibliotheken manchmal viele Workarounds benötigen, um sich auf das Scala-Typsystem abbilden zu lassen, existieren Bibliotheken wie ScalaFX (https://www.scalafx.org/), die Wrapper-Facades nutzen, um Abhängigkeiten von JavaFX zu verbergen.

Ein in Scala exklusives Feature ist der Ausschluss von Objekten beim Import.

import java.util.{Date => _}

Diese Import-Anweisung schließt die Klasse Date vom Import aus. Wer nur wenige Klassen eines Pakets importieren möchte, kann dies ebenfalls spezifizieren:

import java.util.{Date, LinkedList}

Dadurch beschränkt sich das Importieren auf Date und LinkedList .

Case Classes

Einen besonderen Aspekt von Scala bilden die sogenannten Case Classes. Zunächst das Beispiel eines GPS-Typs, der Länge und Breite eines geografischen Orts spezifiziert:

case class GPS(lat : String, lon: String):
override def toString() = s"($lat, $lon)"

val pos1 = GPS("Lat1", "Lon1")
val pos2 = GPS.apply("lat2", "lon2") // alternativer Stil
println(pos1)
println(pos2)
val pos3 = pos1.copy()
println(pos3)

->
(Lat1, Lon1)
(lat2, lon2)
(Lat1, Lon1)

Zu jeder Case Class gibt es ein Kompagnon-Objekt gleichen Namens. Dieses implementiert Methoden wie apply() und copy(). apply() ermöglicht das Kreieren von Instanzen einer Case Class ohne new-Operator, copy() erzeugt eine Kopie des betreffenden Objekts. Beim Anlegen einer neuen Instanz von GPS wird also implizit das Kompagnon-Objekt und dort die Methode apply() benutzt.

Ein Vorteil von Case Classes ist ihre Anwendung im Pattern Matching. Statt es kompliziert zu erklären, soll wieder ein Beispiel das Ganze illustrieren. Zunächst definieren wir das Trait Message und drei Case-Klassen, die allesamt Message implementieren. Da dem Trait Message ein sealed vorangestellt ist, können nur die Entitäten derselben Implementierungsdatei Message nutzen:

sealed trait Message

case class Email(sender: String, subject: String, body: String)
extends Message
case class Twitter(user: String, tweet: String) extends Messagecase
class WebLink(author: String, link: String) extends Message

Eine Methode soll nun beliebige Arten von Case Classes verarbeiten, die Message implementieren:

def showMessage(message: Message): String =
message match
case Email(sender, subject, _) =>
s"Neue Mail von $sender mit dem Subject: $subject"
case Twitter(user, tweet) =>
s"WhatsApp von $user! Inhalt: $tweet"
case WebLink(author, link) =>
       s"Weblink von $author! Besuche: $link"

Die Unterscheidung erfolgt in der match-Anweisung. Für jede Art einer Nachricht lässt sich per case-Klausel auf die Struktur der entsprechenden Case Class zugreifen. Der Unterstrich _ im Falle der Email bedeutet, dass wir uns nicht für die eigentliche Nachricht body interessieren. In jeder case-Anweisung geben wir eine entsprechende Zeichenkette zurück.

Am Schluss des Programms erfolgt die Instanziierung verschiedener Wertobjekte, die wir dann an showMessage() übergeben. Nun erfolgt der Aufruf dieser Funktionalität:

mail = Email(“god@heaven.org”, “Wichtig”, “Sag ich nicht!”)
 tweet = Twitter(“@Michael”,
“Heute ist ein guter Tag, Scala 3 zu lernen”)
link = WebLink(“SIGS-DATACOM”, “Besuche: https://www.javaspektrum.de”)

println(showMessage(mail))
println (showMessage(tweet))
println(showMessage(link))

Als Ausgabe erhalten wir:

->
Neue Mail von god@heaven.org mit dem Subject: Wichtig
WhatsApp von @Michael! Inhalt: Heute ist ein guter Tag, Scala 3 zu lernen
Weblink von SIGS-DATACOM! Besuche: https://www.javaspektrum.de

Übrigens ist match auch für „schlichtere” Anwendungsfälle anwendbar, etwa bei:

def weekday(day: Int) : String =
day match
case 1 => “Montag”
case 2 => “Dienstag”
case 3 => “Mittwoch”
case 4 => “Donnerstag”
case 5 => “Freitag”
case 6 | 7 => “Wochenende”
println(weekday(3))
println(weekday(7))

->
Mittwoch
  Wochenende

Methoden, Funktionen und Lambdas

Methoden beziehungsweise Funktionen definieren Entwickler in Scala mit def. Im nachfolgenden Code besitzt die Methode fac(Int):-BigInt eine interne Methode fact(int, BigInt):BigInt, die die Fakultät berechnet, indem sie alle Zwischenprodukte im Parameter prod speichert. Dadurch lässt sich Tail-Rekursion vermeiden, was den Compiler in die Lage versetzt, den Code zu optimieren:

def fac(n: Int): BigInt =
def fact(n: Int, prod: BigInt): BigInt =
if n <= 1 then prod
else fact(n - 1, n * prod)
  fact(n, BigInt(1))

Die folgende Zeile berechnet die Fakultät für die Zahlen 0 bis 9. Als Parameter der foreach-Methode fungiert ein Lambda i => println(s"-Fakultät von $i: ${fac(i)}"). Durch den Aufruf von

(0 to 9).foreach(i => println(s"Fakultät von $i: ${fac(i)}"))

wird für jeden von foreach vorgegebenen Wert i die entsprechende Fakultät berechnet und ausgegeben. Funktionen stellen in Scala First-Class-Objekte dar, die sich auch als Parameter übergeben, in Variablen speichern oder als Resultate von Methoden zurückliefern lassen.

Am Schluss noch die Ausgabe des obigen Programmcodes:

->
Fakultät von 0: 1
Fakultät von 1: 1
Fakultät von 2: 2
Fakultät von 3: 6
Fakultät von 4: 24
Fakultät von 5: 120
Fakultät von 6: 720
Fakultät von 7: 5040
Fakultät von 8: 40320
  Fakultät von 9: 362880

Das (0 to 9) oben ist ein Beispiel für eine Range. Im konkreten Fall umfasst die Range alle Werte von 0 bis 9. Ein größeres Beispiel ist die Bereitstellung von Grundfunktionalität für einen Auswerter:

val add = (a: Int, b: Int) => a + b
val sub = (a: Int, b: Int) => a-b
val mul = (a: Int, b: Int) => a * b
val div = (a: Int, b: Int) => a / b

println(sub(div(mul(4, add(6,7)), 4), 3))­

->
10

Oder im folgenden Beispiel:

def eval(fun : (Int,Int) => Int, a: Int, b: Int) : Int = fun(a, b)
println(eval(add, 40, 2))
println(eval(mul, 14, 3))
­->
42
42

Funktional programmieren

Der funktionalen Programmierung widmet sich Scala intensiv. Ein wichtiges Instrument dazu ist, Daten unveränderlich zu machen, sodass keine Seiteneffekte entstehen können. Es ist dann garantiert, dass alle Funktionen „pure” sind, also stets denselben Wert bei gleichen Parametern liefern. Aus demselben Grund können Scala-Entwickler für referenzielle Integrität sorgen. Nach Möglichkeit sollten Programmierer daher nur Wertobjekte verwenden, die ein vorangestelltes val enthalten. Ein einfaches Beispiel:

val name = "Java"
name = "Scala" // Compiler meldet Fehler!

Bei der zweiten Zuweisung meldet der Übersetzer den Fehler „Reassignment to val name“. Die Zuweisung eines neuen Werts an ein unveränderbares Datum ist nicht zulässig. Hingegen funktioniert:

var name = "Java"
name = "Scala"

ohne Beanstandung, weil die Vereinbarung name als Variable kennzeichnet.

Eine typische Eigenschaft funktionaler Sprachen ist die Unterstützung von Folding- und Map-Funktionen. Im folgenden Beispiel enthält das Objekt Converter eine Methode convertUpper(), die alle Zeichenketten einer übergebenen Sequenz von Zeichenketten in Großbuchstaben umwandelt. Wir sehen im Code keine Klassendefinition, sondern eine object-Definition. Objekte besitzen im Gegensatz zu Klassen immer nur ein und dieselbe Instanz, weshalb ihre Methoden im Java-Sinne statisch sind. Es handelt sich also um Singletons.

Die Methode map() durchläuft alle Elemente der übergebenen Sequenz und konvertiert diese in Großbuchstaben, wobei sie die konvertierten Zeichenketten wiederum als Sequenz zurückliefert.

object Converter:
def convertUpper (strings: Seq[String]): Seq[String] =
    strings.map((s:String) => s.toUpperCase)

val res = Converter.convertUpper(List("Hallo", "JavaSPEKTRUM"))
println(res)

­->
  List(HALLO, JAVASPEKTRUM)

Mithilfe der filter()-Funktion ist die Suche nach Objekten mit einem vorgegebenen Kriterium möglich, das als Lambda definiert ist. Im folgenden Beispiel filtern wir aus einer Liste von Zeichenketten alle heraus, die mit dem Großbuchstaben M beginnen:

def find(prefix: String, strings: Seq[String]) : Seq[String] =
strings.filter(str => str.startsWith(prefix))

val findString = "M"
val searchList = List("Opel", "BMW", "Mazda", "Mitsubishi", "VW")
val result = find(findString, searchList)
println(result)

->
List(Mazda, Mitsubishi)

Daneben gibt es noch weitere funktionale Methoden, etwa fold() , foldleft() und foldright() . Auch hier wieder ein konkretes Beispiel statt vieler Worte. Die Funktion numbers.foldleft() berechnet das Produkt der Zahlen von 1 bis 5, also die Fakultät von 5:

val numbers: List[Int] = List(1, 2, 3, 4, 5)
numbers.foldLeft(1) { (prod, i) => prod * i }

­->
120
­

foldleft erwartet als Parameter einen Initialwert – hier 1 – und ein Lambda – hier (prod, i) => prod * i. Die Elemente der Liste multipliziert foldleft nacheinander mit prod und merkt sich das Ergebnis als neuen Wert von prod. Das am Ende resultierende prod kennzeichnet somit das Ergebnis der gesamten Operation.

Zum Glück lässt sich der obige Aufruf mit dem Platzhalter „_” vereinfachen zu:

numbers.foldLeft(1) (_ * _)

Scala verfügt im Übrigen über ein ganzes Paket unveränderbarer Collection-Klassen namens scala.collection.immutable, wie etwa String[T], List[T], Range[T] und Vector[T]. Als Elternklasse dieser Collections fungiert Seq[T]. Das bedeutet, dass Programmierer einzelne Collections nicht verändern dürfen. Stattdessen ist bei manipulierenden Operationen stets die Kreierung eines neuen Objekts nötig. Wie in Java entsteht beispielsweise bei jeder manipulierenden Operation mit Zeichenketten ein neuer String.

Veränderbare Collections

Daneben gibt es freilich auch einige veränderbare Collection-Klassen, wozu auch Felder gehören: array[T]. An dem folgenden Programmcode ist das gut zu sehen. In #1# erfolgt die Änderung einer Information (persönliche ID), obwohl die Variable person als unveränderlich definiert wurde. Und in #2# tauschen wir ein ganzes Element des Feldes aus. Das können wir, weil Array[T] einen veränderbaren Datentyp repräsentiert:

class Contact(val name: String, var id : Int)

val addressList = Array(Contact("Michael", 1), Contact("Robert", 2))

val person = addressList(1)
person.id = 3 // #1# Ändern der ID
println(addressList(0).name)
println(addressList(0).id)
println(addressList(1).name)
println(addressList(1).id)

addressList(0) = Contact("Betty", 4) // #2# Ändern eines ganzen Array-
Elements
println(addressList(0).name)
println(addressList(0).id)

->
  Michael
  1
  Robert
  3
  Betty
  4

Wer auf der sicheren Seite bleiben will, benutzt also entweder die unveränderlichen Collection-Klassen oder trifft Vorkehrungen, dass alle Aktionen frei von Seiteneffekten bleiben. Diese Seiteneffekte beinträchtigen auch die Programmierung von Nebenläufigkeit, weil sie besonderer Schutzmaßnahmen bei gemeinsamen „mutable” Objekten bedürfen. Es ist daher wesentlich effektiver und sicherer, konsequent dem funktionalen Paradigma zu folgen. Und das gilt nicht nur für Scala.

Code des Monads

Funktionale Programmiersprachen ergänzen die übliche Fehlerbehandlung mit dem sogenannten Option-Monad. Es ist auf jeden Fall eine wesentlich bessere Alternative als die Rückgabe von null im Fehlerfall. Hier ein Beispiel. Die untere Funktion convert-ToInt() erwartet einen Eingabestring, der eine Zahl speichert. Verbirgt sich tatsächlich eine Zahl hinter der Zeichenkette, so liefert die Funktion Integer.parseInt() ein Ergebnis zurück, ansonsten eine Ausnahme NumberFormatException . Die Methode convertToInt() macht daraus im Erfolgsfall ein Some(ergebnis) und im Fehlerfall ein None . Der Code verwendet den generischen Datentyp Option[T], der zwei Arten von Werten annehmen kann. Im Erfolgsfall ein konkretes Ergebnis Some(123) , im Fehlerfall None:

def convertToInt(in : String) : Option[Int] = {
try {
Some(Integer.parseInt(in.trim))

}
catch {
case e: NumberFormatException => None
}
}
convertToInt(“123”) match
case Some(i) => println(i)
case None => println(“Fehler”)

convertToInt(“abc”) match
case Some(i) => println(i)
case None => println(“Fehler”)
->
Some(123)
Fehler

Der Code verwendet den generischen Datentyp Option[T], der zwei Arten von Werten annehmen kann. Im Erfolgsfall ein konkretes Ergebnis Some(123), im Fehlerfall None.

Den Vorteil von Option sieht man deutlich am folgenden Beispiel. Wir könnten für eine Spalte in Excel mit Zahlen und Zeichenketten alle Zahlen aufsummieren wollen. Die funktionale Operation flatM-ap() leistet genau dies, weil sie sich die Semantik von Option zunutze macht, und nur alle Some-Ergebnisse, aber kein None in die Summe einbezieht:

def convertToInt(in : String) : Option[Int] = {
try {
Some(Integer.parseInt(in.trim))
}
catch {
case e: NumberFormatException => None
}
}

val bag = List(1, “abc”, 5, “def”, 4, “ghi”)
val sum = bag.flatMap(convertToInt).sum

->
   10

Fazit

Dieser Artikel hat gerade einmal die Spitze des Eisbergs gestreift. Viele essenzielle Ingredienzen von Scala 3 wie Implicits, Tests, Covariance und Contravariance sind aus Gründen der Länge nicht vorgekommen. Es ging aber nicht um Vollständigkeit, sondern darum, eine Art Appetizer für Scala 3 bereitzustellen.

Wie stellt sich also die Situation dar? Um die Gunst der JVM-fokussierten Entwickler konkurrieren mittlerweile eine ganze Menge von Programmiersprachen, darunter beispielsweise Kotlin, Clojure und Groovy. Die Sprache Scala eignet sich für diejenigen, die den mächtigen Paradigmenansatz und die Skalierbarkeit schätzen. In der Version 3 haben sich die Entwickler der Sprache getraut, einige Sprachmerkmale rigoros an die Bedürfnisse der Nutzer anzupassen. Dank der mittlerweile verfügbaren Werkzeuge stehen diverse Alternativen für die Scala-Entwicklung zur Verfügung.

Ein weiteres Argument für Scala ist die wachsende Zahl von Anwendungen, die Scala verwenden. Herausragende Beispiele sind Apache Spark und Apache Kafka sowie Akka. Darüber hinaus setzen etliche Firmen Scala ein, etwa Twitter, LinkedIn, Morgan Stanley, The Guardian und Sony. Daher gibt es genug Gründe, Scala als ernsthaften Kandidaten für Projekte zu betrachten.

. . .
Vorheriger Artikel
Cloud-Computing im Großkonzern
Nächster Artikel
Architektur für Frontends

Author Image

Michael Stal

Chefredakteur von JavaSPEKTRUM
Zu Inhalten

Prof. Dr. Michael Stal beschäftigt sich bei der Corporate Technology der Siemens AG mit Software- und Systemarchitekturen, Digitalisierung und KI. An der University of Groningen hält er Vorlesungen und betreut Doktoranden. Außerdem ist er Chefredakteur von JavaSPEKTRUM.


Artikel teilen

Nächster Artikel
Architektur für Frontends