Ich lerne Scala – Tag 4

4 minute read Published:

Dieser Artikel stammt aus meinen früheren Wordpress-Instanzen und steht hier aus Gründen der Nostalgie.

Heute habe ich wieder etwas Zeit gefunden, mich mit der Programmiersprache Scala zu beschäftigen. Seit einiger Zeit lese ich das Buch „Programming in Scala“ von den Machern der Sprache. Zuletzt hatte ich wenig Zeit, so dass seit dem letzten Eintrag zum Thema eine ganze Weile vergangen ist.

Im heute besprochenen Kapitel 4 geht es um Klassen und Objekte.

Eine Klasse in Scala ist wie in Java eine Blaupause, aus der sich Objekte erzeugen lassen. Eine Klasse wird mit dem Schlüsselwort class definiert.

1
2
3
class Planet {
...
}

Eine Klasse kann Instanzvarablen und Instanzmethoden haben. Instanzvariablen, so sie veränderlich sind, werden mit var eingeleitet, ansonsten wird val verwendet. Instanzmethoden werden mit def definiert.

1
2
3
4
5
6
class Planet {

  var a =

  def calcForce(mass: Double): Double = mass * a
}

Was es im Gegensatz zu Java in Scala nicht gibt, sind statische Variablen und Methoden. Stattdessen ist es in Scala möglich ein Singleton-Objekt zu definieren. Dieses wird mit dem Schlüsselwort object eingeleitet.

1
2
3
object Magrathea {
  def buildPlanet(hasFjorde: Boolean): Planet = new Planet(9.81)
}

Im Code kann dann z.B. folgendes gemacht werden:

1
Magrathea.buildPlanet(false).calcForce(1000)

Hat das Singleton-Objekt den gleichen Namen wie eine Klasse, dann wird es als Kompagnon-Objekt bezeichnet. Z.B. besitzt die Klasse Array ein Kompagnon-Objekt Array mit einer Methode apply, die verwendet wird um Instanzen der Klasse Array zu erstellen, indem man

1
var myArr = Array("elem1", "elem2", "elem3") aufruft.

Klassen müssen in Scala nicht in gleichnamigen Dateien definiert sein, wie das in Java der Fall ist. Es ist aber guter Stil das zu tun.Eine Klasse und ihr Kompagnon müssen aber in der selben Datei stehen. Klassen und Objekte aus anderen Dateien importiert man über das import-Statement. Dieses ist um einiges mächtiger als sein Äquivalent in Java. Mit ihm lassen sich neben Klassen und Singleton-Objekten auch Methoden importieren, die dann über den einfachen Namen direkt aufrufbar sind. Dies ist etwa vergleichbar mit einem import static in Java, funktioniert aber für alle Objekte, und da in Scala alles ein Objekt ist, funktioniert es folglich für alles.

Semikolons sind in Scala meist unnötig. Normalerweise wird ein Zeilenende schlicht als Semikolon interpretiert. Wenn man mehrere Anweisungen in eine Zeile schreiben will, muss man diese Anweisungen allerdings mit Semikolons trennen.

Zeilenende wirken wie gesagt als Semikolon-Ersatz. Es gibt hier allerdings Ausnahmen:

  • Die Zeile endet mit einem Wort, dass nicht als gültiges Zeilenende durchgeht, bspw. ein Punkt oder ein Infix-Operator
  • Die nächste Zeile startet mit einem Wort, dass kein gültiger Zeilenanfang sein kann
  • Die Zeile endet in einer geöffneten runden oder eckigen Klammer

In Scala ist es daher üblich, mehrzeilige Anweisungen hinter dem Operator zu trennen:

1
2
3
x +
y +
z

Mit Scala kann man „Skripte“ programmieren, das hatten wir schon. Allerdings kann man auch ganze Anwendungen damit bauen. Jede gute Anwendung braucht einen Startpunkt, und in Scala ist das eine Klassenmethode namens main mit einem Parameter vom Typ Array[String]. Diese Methode muss in einem Singleton-Objekt definiert werden.

1
2
3
4
5
object MyApp {

  def main(args: Array[String]) {for(arg <- args) println(arg)}

}

Dieser Code gibt die Kommandozeilenparameter jeweils in einer eigenen Zeile aus. Wichtig ist noch, dass man die Anwendung nicht durch einen Aufruf von

1
scala MyApp.scala

starten kann. In diesem Fall würde scala MyApp.scala als ein Skript ohne Anweisung (es wird ja nur eine Klasse definiert, aber sonst keine Anweisung ausgeführt) interpretieren. Mittels scalac muss zuerst Bytecode erzeugt werden und dann die Klasse mittels scala gestartet werden:

1
2
scalac MyApp.scala
scala MyApp # Achtung, die Dateiendung muss weggelassen werden

scalac lässt sich leider etwas Zeit, weil beim Start zig JAR-Dateien und das aktuelle Verzeichnis gescannt werden müssen. Aus diesem Grund gibt es fsc, Fast Scala Compiler. fsc ist ein Daemon-Prozess, der einmalig die Umgebung initialisiert und dann beim zweiten und jedem weiteren Aufruf nur noch die eigentliche Übersetzung macht:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[ninan@hawking scalatut]$ time fsc MyApp.scala

real    0m6.710s
user    0m0.463s
sys     0m0.048s
[ninan@hawking scalatut]$ rm MyApp.class
[ninan@hawking scalatut]$ time fsc MyApp.scala

real    0m0.835s
user    0m0.469s
sys     0m0.038s
[ninan@hawking scalatut]$ scala MyApp ich bin ein parameter
ich
bin
ein
parameter
[ninan@hawking scalatut]$ fsc -shutdown
[Compile server exited]

Das war’s für heute mit Scala.