Prolafex Nr. 2: Elvis und Band

Von der Nützlichkeit des Null-Pointers kann man ja halten, was man will, aber es macht das Programmieren doch etwas unübersichtlicher. Kein Wunder, dass in einigen Sprachen der Elvis-Operator Abhilfe schaffen muss.

In Pocket speichern vorlesen Druckansicht 6 Kommentare lesen
Lesezeit: 4 Min.
Von
  • Michael Wiedeking

Von der Nützlichkeit des Null-Pointers kann man ja halten, was man will, aber es macht das Programmieren doch etwas unübersichtlicher. Kein Wunder, dass in einigen Sprachen der Elvis-Operator Abhilfe schaffen muss.

Mehr Infos

Pro·la·fex, das; -, -e <engl.-lat.> [Abk. für : programming language feature extra ordinem]: Element einer Programmiersprache, das aus verschiedensten Gründen besondere Beachtung verdient – ohne jedermann gefallen zu müssen.

Bei der Erfindung der Null-Referenz mag es sich ja um einen Milliarden-Fehler handeln, aber jetzt ist der Null-Pointer nun mal da, und wir haben damit zu leben. Das allerdings hat viele unangenehme Begleiterscheinungen. Nicht nur, dass etwa in Java die NullPointerException zu den häufigsten Laufzeitfehlern im Produktionsbetrieb gehören soll, das Programmieren selbst stellt sich übermäßig geschwätzig dar.

Hat etwa ein Person-Objekt ein Address-Object und diese eine Street-Objekt, das ein Number-Objekt enthält, dann kann sich ein Zugriff auf das letzte Element als sehr aufwendig gestalten, wenn man sich dazu entschieden hat, dass alle Referenzen dieser Zugriffskette auch null sein dürfen. Denn dann muss, um eine NullPointerException vermeiden zu können, jedes einzelne Glied auf seine Brauchbarkeit hin untersucht werden.

Person p = ...;
Number n = null;
if (p != null) {
Address a = p.getAddress();
if (a != null) {
Street s = a.getStreet();
if (s != null) {
n = s.getNumber();
}
}
}

Wenn man dies ein paar mal machen musste, hat man viel geschrieben und dabei doch relativ wenig erreicht (wenn man mal davon absieht, dass die Verwendung der Getter hier höchst zweifelhaften Charakters ist).

In einigen Programmiersprachen geht man nun dazu über, dass ein Wert eines bestimmten Typs T nur dann null sein kann, wenn er explizit Nullable ist. In vielen Programmiersprachen, wie auch in Fantom, geschieht das durch Ergänzen des Typnamens durch ein Fragezeichen: T?. Damit wird verhindert, dass eine unerwünschte Null-Referenz frühzeitig an Ort und Stelle erkannt werden kann und sie nicht irgendwo im Programm zu einem Fehler führen kann, wo die Ursache nicht mehr auf Anhieb erkannt werden kann.

Das löst natürlich obiges Schreibproblem nicht. Aber auch hier weiß Fantom Abhilfe zu schaffen. In Fantom gibt es nämlich auch – wie in vielen an die Syntax von C und C++ angelehnten Sprachen – den dubiosen Ternäroperator (B) ? T : F, der für eine Bedingung B im Erfolgsfall nur den Ausdruck T auswertet und dessen Ergebnis liefert und andernfalls nur F berechnet, um sein Resultat zu liefern.

Address? a := (p != null) ? p.getAddress() : null
Street? s := (a != null) ? a.getStreet() : null
Number? n := (s != null) ? s.getNumber() : null

(Nur für den Fall, dass sich jemand wundert: Anweisungen in Fantom müssen nicht durch ein Semikolon abgeschlossen werden. Der Zuweisungsoperator := erlaubt in Fantom das Deklarieren lokaler Variablen. Der Doppelpunkt soll Schreibfehler verhindern und macht den Typen optional. Damit könnte auch

a := (p != null) ? p.getAddress() : null

geschrieben werden, womit der Typ automatisch als Address? erschlossen würde.)

Damit ist für alle Sprachen, die diesen Ternäroperator haben, obiger Code schon deutlich übersichtlicher geworden, aber für die Fantom-Designer war das wohl noch nicht gut genug. Mithilfe des sogenannten Elvis-Operators ?:, der seinen Namen von dem gleichnamigen Emoticon bezieht, kann auf das gewünschte Feld oder die entsprechende Methode zugegriffen werden, ohne dass die null-Prüfung explizit gemacht werden muss.

Address? a := p ?: p.getAddress()
Street? s := a ?: a.getStreet()
Number? n := s ?: s.getNumber()

Damit lassen sich schon viele Ausdrücke merklich vereinfachen. Etwa wenn es zu einem Schlüssel in einer Map keinen passenden Wert gibt und dies durch das Liefern von null signalisiert wird, dann lässt sich diese kompakt durch einen Default-Wert ersetzen, indem man

return map[key] ?: defaultValue

schreibt. Leider macht diese Schreibweise, wie im Person-Beispiel zu sehen, noch nicht die Einführung der temporären Variablen überflüssig. Aber auch hier gibt es einen verwandten Operator, der einen "sicheren Aufruf" gewährleistet:

Number? n := p?.getAddress()?.getStreet()?.getNumber()

Nun ist n entweder die ersehnte Nummer oder aber null; eine NullPointerException kann aber auf keinen Fall auftreten.

Was also in anderen Programmiersprachen zu einem Laufzeitfehler führen könnte, wird in Fantom also vollständig eliminiert, indem der Programmierer durch das Typsystem und darauf abgestimmte Operatoren nahezu optimal unterstützt wird. ()