Sichtbarkeit

Sichtbarkeitsmodifizierer
Packages
Elelemente (Klassen ...) importieren

Sichtbarkeitsmodifizierer

Wer aus der Java-Programmierung kommt, dem dürfte beim Studium von Scala Quelltexten auffallen, das sehr wenig Sichtbarkeitsmodifizerer wie public, private oder protected verwendet werden. Wird in Scala kein Sichtbarkeitsmodifizierer angegeben, so handelt es sich um die Sichtbarkeitsstufe public, welche für alle Klassen sichtbar ist. Das Weglassen des Sichtbarkeitsmodifizierers in Java würde zur Default-Sichtbarkeit führen, die so in Scala nicht definiert ist. Die Sichtbarkeitsstufe public ist in Scala Standard. Ein Sichtbarkeitsmodifizierer public wird in Scala nicht angegeben. Eine Angabe würde sogar zu einem Compiler-Fehler führen.

Für Elemente, die mit der Sichtbarkeitsstufe private gekennzeichnet sind, gilt, dass diese Elemente nur innerhalb Ihrer eigenen Klasse sichtbar sind.

Die Sichtbarkeitsstufe protected definiert eine Sichtbarkeit der Elemente innerhalb der eigenen Klasse und in allen von Ihr abgeleiteten Klassen. Im Unterschied zu Java sind mit protected gekennzeichnete Elemente nicht innerhalb Ihres eigenen Packages für andere Klassen sichtbar.

Bei den Sichtbarkeitsmodifizierern private und protected kann innerhalb folgenden, rechteckigen Klammern, die Sichtbarkeit feiner eingestellt bzw. ausgeweitet werden. So ist es z.B. möglich die Sichtbarkeit auf eine fest definierte andere Klasse (oder ein anderes Package) auszuweiten (private[X] def .... Auch das Einschränken der Sichtbarkeit auf das eigene Objekt ist mit private[this] ... möglich, sodass andere Objekte der gleichen Klasse nicht auf das jeweilige Element zugreifen können.

Insgesamt lässt sich feststellen, dass die Sichtbarkeit in Scala wesentlich feiner eingestellt werden kann, als dies bei Java der Fall ist.

class ScalaVisibility {
  // Sichtbarkeitsstufe public: von überall aufrufbar
  def myPublicFunction : Int = 1
  
  // Sichtbarkeitsstufe protected: Nur für die eigene und abgeleitete
  // Klassen sichtbar
  protected def myProtectedFunction : Int = 2
  
  // Sichtbarkeitsstufe private: nur innerhalb der eigenen Klasse
  // sichtbar
  private def myPrivateFunction : Int = 3
}

class myTestClass{
  val visio = new ScalaVisibility
  
  println(visio.myPublicFunction)         // OK
  println(visio.myProtectedFunction)      // Fehler
  println(visio.myPrivateFunction)        // Fehler
}

class myTestClass2 extends ScalaVisibility{
  val visio = new ScalaVisibility
  
  println(visio.myPublicFunction)        // OK     
  println(visio.myProtectedFunction)     // Fehler
  println(visio.myPrivateFunction)       // Fehler
  println(myProtectedFunction)           // OK
  println(myPrivateFunction)             // Fehler
}
timpt.de - X2H V 0.11

Packages

Selbst in kleineren Projekten steigt die Anzahl der verwendeten Klassen, Traits, Singleton Objekte ... schnell auf ein Maß an, dass die Übersichtlichkeit darunter leidet. Um nun den Überblick zu behalten, bietet es sich an, Klassen in Packages zu organisieren. Die Package-Struktur entspricht dabei der Struktur aktueller Dateisysteme, wo eine Baumstruktur aufgebaut wird und die Elemente in den Unterschiedlichen Ebenen abgelegt werden. Alle Elemente, die sich in einer Ebene befinden, haben dabei eine zusammengehörige Aufgabe.

Auch wenn in Scala weit aus flexibler mit Packages gearbeitet werden kann, bietet es sich für den Anfang an, die Vorgehensweise von Java zu übernehmen und jede Klasse (Trait ...) am Anfang des Quelltextes einem Package zuzuordnen.

package de.scalatutorial.berechnung.helfer

class Rechenknecht {
  // Inhalt der Klasse
}
timpt.de - X2H V 0.11

Die Definition eines Packages beginnt mit dem Schlüsselwort package gefolgt vom Package-Namen. Die Namen der einzelnen Baumebenen werden durch einen Punkt voneinander getrennt. Im obigen Beispiel definieren wir ein Package in der vierten Ebene. Vorausgesetzt unser Quelltext Root Verzeichnis entspricht C:\projekte\scalatutorial\src entspricht die obige Package Definition dem Verzeichnis C:\projekte\scalatutorial\de\scalatutorial\berechnunug\helfer .

Gibt man keine Package-Definition im Quelltext an, so befinden sich die enthaltenen Elemente im sogenannten Default-Package. Auch wenn die meisten Beispiele hier auf scalatutorial.de ohne Package Angabe gezeigt werden (dies dient der Kürze und Verständlichkeit), wird von dessen Verwendung in realen Projekten abgeraten.

Eine Konvention besagt, dass Package Namen so gewählt werden sollen, dass Sie der umgekehrten qualifizierten Schreibweise einer zugehörigen Web-Präsenz entsprechen. Dem entspricht, dass die Beispiele auf scalatutorial.de als Basis Package de.scalatutorial haben sollten. Hintergrund dieser Konvention ist, dass damit sichergestellt werden kann, dass die Verwendung von Klassen (Traits ...) aus verschiedenen Quellen, nicht zu Namenskonflikten führen kann. Der vollständig qualifizierende Namen einer Klasse besteht zum Beispiel aus Package Namen und Klassen Namen. Der voll qualifizierende Name der Klasse aus obigem Beispiel ist somit de.scalatutorial.berechnung.helfer.Rechenknecht . Auch wenn diese Konvention nicht für alle Projekte verwenden kann, sollte man Sie jedoch in Projekten anwenden, dessen Quellen man für andere Projekte (anderer Personen, Teams) zur Verfügung stellen möchte.

Elemente (Klassen ...) importieren

Die meisten Elemente einer Scala-Blibliothek wie Klassen oder Traits können wir nicht direkt, ohne Angabe des Packages, in unserem Quelltext verwenden. Beispielsweise können wir keine Instanz der Klasse scala.collection.mutable.ListBuffer direkt in der REPL (Scala Interpreter) erzeugen.

scala> val listBuffer = new ListBuffer[String]
<console>:7: error: not found: type ListBuffer
       val listBuffer = new ListBuffer[String]
timpt.de - X2H V 0.11

Um dies zu tun, können wir den voll qualifizierten Namen, d.h. Package-Name plus Klassenname, verwenden.

scala> val listBuffer = new scala.collection.mutable.ListBuffer[String]
listBuffer: scala.collection.mutable.ListBuffer[String] = ListBuffer()
timpt.de - X2H V 0.11

Jedes Mal den voll qualifizierten Namen anzugeben ist nicht nur aufwendig, sondern führt auch zu schwerer lesbaren Quelltext. Möchten wir nun die Klasse ListBuffer, ohne Angabe des Packages, verwenden, müssen wir diese zunächst in den Sichtbarkeitsbereich unseres aktuellen Kontextes (hier REPL) holen. Helfen tut uns hierbei das Schlüsselwort import, mit dem wir Klassen in den Sichtbarkeitsbereich holen können. Um nun die Klasse ListBuffer in den Sichtbarkeitsbereich zu holen, verwenden wir import, gefolgt vom Namen der zu importierenden Klasse. Da noch keine (hier helfenden) import-Anweisungen ausgeführt worden sind, müssen wir den voll qualifizierten Namen der Klasse angeben. Nachdem wir dies getan haben, können wir von der Klasse ListBuffer Instanzen (Objekte), ohne Angabe des Packages, erzeugen.

scala> import scala.collection.mutable.ListBuffer
import scala.collection.mutable.ListBuffer

scala> val listBuffer = new ListBuffer[String]
listBuffer: scala.collection.mutable.ListBuffer[String] = ListBuffer()
timpt.de - X2H V 0.11

Möchten wir mit mehreren Elementen (z.B. Klassen) eines Packages arbeiten, können wir alle Elemente eines Paketes mit dem Namen des Packages, gefolgt vom Unterstrich, in den aktuellen Sichtbarkeitsbereich holen.

scala> import scala.collection.mutable._
import scala.collection.mutable._

scala> val aLinkedList = new LinkedList[String]
aLinkedList: scala.collection.mutable.LinkedList[String] = LinkedList()
timpt.de - X2H V 0.11

Möchten wir mehrere Elemente eines Packages, aber nicht alle, importieren, können wir das in Scala mit einem einzigen import-Statement tun. Dazu geben wir wie gewohnt zunächst das import-Statement gefolgt vom Packagenamen und einem abschließenden Punkt an. Danach geben wir die zu importierenden Elemente des Packages in geschweiften Klammern durch ein Komma getrennt an. Im nachfolgenden Beispiel importieren wir die Klassen ListBuffer, HashMap und HashSet des Packages scala.collection.mutable. Alle anderen Elemente (Klassen) werden durch dieses import-Statement nicht importiert.

scala> import scala.collection.mutable.{ListBuffer, HashMap, HashSet}
import scala.collection.mutable.{ListBuffer, HashMap, HashSet}
timpt.de - X2H V 0.11