Typhierarchien
Julia bietet einen minimalistischen Vererbungsmechanismus, den Listing 3 skizziert. Zeile 4 definiert den abstrakten Datentyp Audio. Ein solcher abstrakter Datentyp kann keine Attribute besitzen, darf aber nur von abstrakten Typen erben. Es ist also nicht möglich, gemeinsame Eigenschaften verschiedener Typen über Vererbung zu organisieren.
Listing 3
# file inherit.jl import Base.show abstract type Audio end struct Song <: Audio name::String end struct Audiobook <: Audio name::String end play(s::Song) = "lala" play(s::Audiobook) = "blabla" show(io::IO, a::Audio) = print(io, a.name)
Die Typen Song und Audiobook erben von Audio. Die Zeilen 15 und 16 definieren verschiedene Methoden play() für Argumente vom Typ Song und Audiobook.
Die Methode show() kommt immer dann zum Zug, wenn die String-Darstellung eines Objektes gefragt ist, zum Beispiel bei einem Aufruf von println(). Zeile 17 definiert show() auf Argumente des Typs Audio für eine maßgeschneiderte String-Ausgabe. Ähnlich wie bei der Multiplikation erfordert das den Import von Base.show().
Das Beispiel zeigt, dass die Vererbung für Julia lediglich dazu dient, unterschiedliche Typen bei Bedarf gemeinsam zu behandeln: play() ist auf die konkreten Typen hin implementiert, show() auf den abstrakten Typ Audio, der Song und Audiobook zusammenfasst.
Listing 4 demonstriert wiederum die Anwendung im interaktiven Modus. Sie sehen, dass bereits die Ausgabe nach dem Konstruktoraufruf die spezifische show-Methode von Audio aufruft. methods(play) informiert, dass es zwei play-Methoden gibt und zeigt die entsprechenden Dateipfade an.
Listing 4
> include("inherit.jl"); > a = Audiobook("Plato: Menon") Plato: Menon > s = Song("ABBA: S.O.S") ABBA: S.O.S > methods(play) # 2 methods for generic function "play": [1] play(s::Audiobook) in Main at ... [2] play(s::Song) in Main at ... > play(a) "blabla" > play(s) "lala" > [a, s] 2-element Array{Audio,1}: Plato: Menon ABBA: S.O.S
Fasst man Songs und Audiobooks mit [a, s] in einem Array zusammen, erkennt Julia den spezifischsten gemeinsamen Supertyp dieser Objekte, nämlich Audio. Bei Objekten, die sich nicht in dieser Form auf einen Nenner bringen lassen, wäre der Typ des Arrays Any.
Möchten Sie mehr über Typen in Julia erfahren, werden Sie in der offiziellen Dokumentation fündig [3]. Zu den für fortgeschrittene Anwender wichtigen, hier ausgelassenen Themen zählen parametrische Typen, primitive Typen und Unions.
Module und Namensräume
Wie jede halbwegs moderne Programmiersprache modularisiert auch Julia Programme und Namensräume über das Schlüsselwort module. Listing 5 definiert das Modul Adder. Es enthält die beiden Methoden add_sugar() und add_salt().
Listing 5
# file adder.jl module Adder export add_sugar add_sugar(x) = string(x)*" + sugar" add_salt(x) = string(x)*" + salt" end
Diese wandeln das übergebene Argument in einen String um und fügen "+ sugar" beziehungsweise "+ salt" hinzu. Die String-Verknüpfung läuft per Asterisk (*). Die Bedeutung der export-Anweisung verdeutlicht Listing 6.
Listing 6
> include("adder.jl") Main.Adder > Adder.add_sugar("tea") "tea + sugar" > Adder.add_sugar(7) "7 + sugar" > Adder.add_salt("fries") "fries + salt" > using .Adder > add_sugar("coffee") "coffee + sugar" > add_salt("soup") ERROR: UndefVarError: add_salt not defined
Wie gehabt fügen wir die Datei adder.jl per include() ein, was es erlaubt, über den Namen des Moduls auf dessen Funktionen zuzugreifen: Adder.add_sugar(). Knapper schreibt es sich, wenn Sie das Modul per using .Adder einbinden, äquivalent mit dem Ausdruck using Main.Adder.





