Ich lerne Scala – Tag 4
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.
<td>
<div class="scala codecolorer">
<a href="http://scala-lang.org"><span class="kw1">class</span></a> Planet <span class="br0">{</span><br /> ...<br /> <span class="br0">}</span>
</div>
</td>
</tr>
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.
<td>
<div class="scala codecolorer">
<a href="http://scala-lang.org"><span class="kw1">class</span></a> Planet <span class="br0">{</span><br /> <br /> <a href="http://scala-lang.org"><span class="kw1">var</span></a> a <span class="sy0">=</span> <span class="nu0"></span><br /> <br /> <a href="http://scala-lang.org"><span class="kw1">def</span></a> calcForce<span class="br0">(</span>mass<span class="sy0">:</span> Double<span class="br0">)</span><span class="sy0">:</span> Double <span class="sy0">=</span> mass <span class="sy0">*</span> a<br /> <span class="br0">}</span>
</div>
</td>
</tr>
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.
<td>
<div class="scala codecolorer">
<a href="http://scala-lang.org"><span class="kw1">object</span></a> Magrathea <span class="br0">{</span><br /> <a href="http://scala-lang.org"><span class="kw1">def</span></a> buildPlanet<span class="br0">(</span>hasFjorde<span class="sy0">:</span> Boolean<span class="br0">)</span><span class="sy0">:</span> Planet <span class="sy0">=</span> <a href="http://scala-lang.org"><span class="kw1">new</span></a> Planet<span class="br0">(</span><span class="nu0">9.81</span><span class="br0">)</span><br /> <span class="br0">}</span>
</div>
</td>
</tr>
Im Code kann dann z.B. folgendes gemacht werden:
<td>
<div class="scala codecolorer">
Magrathea.<span class="me1">buildPlanet</span><span class="br0">(</span><a href="http://scala-lang.org"><span class="kw1">false</span></a><span class="br0">)</span>.<span class="me1">calcForce</span><span class="br0">(</span><span class="nu0">1000</span><span class="br0">)</span>
</div>
</td>
</tr>
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
<td>
<div class="scala codecolorer">
<a href="http://scala-lang.org"><span class="kw1">var</span></a> myArr <span class="sy0">=</span> Array<span class="br0">(</span><span class="st0">"elem1"</span>, <span class="st0">"elem2"</span>, <span class="st0">"elem3"</span><span class="br0">)</span> aufruft.
</div>
</td>
</tr>
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:
<td>
<div class="scala codecolorer">
x +<br /> y +<br /> z
</div>
</td>
</tr>
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.
<td>
<div class="scala codecolorer">
<a href="http://scala-lang.org"><span class="kw1">object</span></a> MyApp <span class="br0">{</span><br /> <br /> <a href="http://scala-lang.org"><span class="kw1">def</span></a> main<span class="br0">(</span>args<span class="sy0">:</span> Array<span class="br0">[</span>String<span class="br0">]</span><span class="br0">)</span> <span class="br0">{</span><a href="http://scala-lang.org"><span class="kw1">for</span></a><span class="br0">(</span>arg <span class="sy0"><</span>- args<span class="br0">)</span> println<span class="br0">(</span>arg<span class="br0">)</span><span class="br0">}</span><br /> <br /> <span class="br0">}</span>
</div>
</td>
</tr>
Dieser Code gibt die Kommandozeilenparameter jeweils in einer eigenen Zeile aus. Wichtig ist noch, dass man die Anwendung nicht durch einen Aufruf von
<td>
<div class="bash codecolorer">
scala MyApp.scala
</div>
</td>
</tr>
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:
<td>
<div class="bash codecolorer">
scalac MyApp.scala<br /> scala MyApp <span class="co0"># Achtung, die Dateiendung muss weggelassen werden</span>
</div>
</td>
</tr>
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:
<td>
<div class="bash codecolorer">
<span class="br0">[</span>ninan<span class="sy0">@</span>hawking scalatut<span class="br0">]</span>$ <span class="kw1">time</span> fsc MyApp.scala <br /> <br /> real 0m6.710s<br /> user 0m0.463s<br /> sys 0m0.048s<br /> <span class="br0">[</span>ninan<span class="sy0">@</span>hawking scalatut<span class="br0">]</span>$ <span class="kw2">rm</span> MyApp.class <br /> <span class="br0">[</span>ninan<span class="sy0">@</span>hawking scalatut<span class="br0">]</span>$ <span class="kw1">time</span> fsc MyApp.scala <br /> <br /> real 0m0.835s<br /> user 0m0.469s<br /> sys 0m0.038s<br /> <span class="br0">[</span>ninan<span class="sy0">@</span>hawking scalatut<span class="br0">]</span>$ scala MyApp ich bin ein parameter<br /> ich<br /> bin<br /> ein<br /> parameter<br /> <span class="br0">[</span>ninan<span class="sy0">@</span>hawking scalatut<span class="br0">]</span>$ fsc <span class="re5">-shutdown</span><br /> <span class="br0">[</span>Compile server exited<span class="br0">]</span>
</div>
</td>
</tr>
Das war’s für heute mit Scala.