Ursachen
- Null ist in vielen Sprachen wie Java vereinbarter Kontrakt "optionaler" Werte, d.h. von Werten die vllt nicht da sind. Ein Beispiel dafür sind Lookup-Operationen, bei denen ich mit einem Schlüssel nach einem Wert zu suche: Diese Operation muss nicht immer einen Wert zurückliefern - falls es zu dem Schlüssel keinen Wert gibt muss ich "nichts" zurücklieferen. => null. (Bsp: java.util.Map.get(key))
- Nicht initialisierte Werte: Null wird auch dann benutzt wenn ein Wert eines Datentyps, z.B. die Property von einem Objekt, im Konstruktor nicht gesetzt wurde. Standardmäßig setzen viele Programmiersprachen in diesem Fall den default "null" => wieder "nichts".
- Designfehler: Während die beiden bisherigen Varianten meist in der Sprache vereinbart sind, gibt es auch noch den dritten Fall: Hilflosigkeit. Viele Menschen, so auch Entwickler, scheinen Fehler lieber vertuschen zu wollen statt um Hilfe zu schreien. In Programmcode ausgedrückt bedeutet dies, dass sie in Objektfeldern null setzen oder aus Methoden null zurückgeben wenn etwas fehlgeschlagen ist dass funktionieren muss. Ein Beispiel dafür sind kritische Initialisierungsroutinen ohne die das Programm nicht durchgeführt werden kann, die aber z.B. durch Fehler in Dateien dennoch fehlschlagen können. Was ist die Konsequenz: => in irgendeinem Feld lauert jetzt.. null.
- Leerer Defaultwert
- "Nichts"-Rückgabewert
- Ergebnis einer fehlgeschlagenenen Operationen.
Werden wir für immer dazu verdammt sein, unseren Code mit Nullchecks an den merkwürdigsten Stellen auszustatten und vor jedem Aufruf einer Funktion Stoßgebete an den Vater der Maschine senden müssen?
Hm. Ich denke es gibt durchaus erfolgversprechendere Lösungen.
Warum ist null kein Fehlerwert, und was macht man stattdessen?
Eliminieren wir von den zuerst genannten drei Fällen direkt die Nummer #3.
null als Fehlerwert zu verwenden, wenn etwas fehlschlägt ist:
(a) die häufigste Ursache von NoPE'. Man rechnet nicht mit Nullwerten an diesen Stellen und kann meist gar nicht weitermachen, wenn bestimmte kritische Werte auf einmal null sind. Eine Nullprüfung würde hier meist nicht weiterhelfen.
(b) Sachlich falsch. Was vorliegt, ist meist einfach nur ein Fehler, und kein leerer Wert.. Die einzig vernünftige Reaktion auf einen Fehler ist aber das Werfen einer Exception (=> eskalieren statt aussitzen). Wenn etwas unerwartet fehlschlägt, ist dies die einzige Art und Weise (in Java und C#) dies eindeutig zu kommunizieren und auch direkt klarzumachen, von wo der Fehler gekommen ist.
Also: Werft eine (checked) Exception wenn etwas nicht behandelbar ist!
Vergesst null für diesen Fall! Der Caller wird meist dazu gezwungen sein, auf null mit einer Exception zu reagieren. Da könnt ihr ihm gleich zuvorkommen.
Warum ist Null als "Nichts" einfach nur falsch?
Es bleiben nur noch zwei Fälle übrig, die semantisch aber effektiv dasselbe Konstrukt behandeln: Null wird hier gleichgesetzt mit "nichts", d.h. null repräsentiert die Unterscheidung zwischen einem gesetzten Wert und dem Fehlen desselben. Dieses Verhalten ist, auch wenn es sich in fast allen Programmiersprachen durchgesetzt hat, sachlich vollkommen falsch.
Ein Hinweis darauf, dass null nicht "nichts" íst, liefert schon der Compiler: Er ist nämlich in allen Sprachen, die null-referenzen erlauben, nicht in der Lage zwischen null und einem gesetzten Wert zur Compilezeit zu unterscheiden. Ergo: Die falsche Verwendung von null kann nicht durch einen Compiler geprüft werden! Den besten Hinweis darauf liefert die Tatsache, dass die Verwendung von null als Wert zu einem Laufzeitfehler führt, nämlich zu einer NoPE!
Laufzeitfehler sind aber, der "hochgelobten" Typprüfung zum Trotz, nichts anderes als Hinweise darauf dass hier das Typsystem der statischen Sprachen wie Java schlicht und ergreifend unvollständig ist. Das mag einer der Gründe dafür sein dass dynamische Sprachen sich einer solchen Beliebheit erfreuen: Die Typsysteme der meisten statischen Sprachen sind an den kritischsten Stellen löchrig: Es gibt Konstrukte, die Compilersicherheit auch bei optionalen Werten ermöglichen. (Dazu später mehr). Dennoch wurde sich bei Sprachen wie Java von vornherein dagegen entschieden diese anzubieten.
Aber warum sollte man ein statisches Typsystem verwenden, dass einen an den wichtigen Stellen unnötig im Stich lässt?
Ich werde die korrekte und triviale "Nichts"-Interpretation, die für optionale Werte nötig ist, in meinen nächsten Post vorstellen. Sie stammt ursprünglich und zwangsläufig aus den funktionalem Umfeld Haskells, das den Wert "null" für Referenzen gar nicht kennt.
No comments:
Post a Comment