Warum wird eine optionale Option verwendet, um die Variable auf Null zu prüfen?

25

Nehmen Sie die beiden Codebeispiele:

if(optional.isPresent()) {
    //do your thing
}

if(variable != null) {
    //do your thing
}

Soweit ich das beurteilen kann, besteht der offensichtlichste Unterschied darin, dass für das optionale Objekt ein zusätzliches Objekt erstellt werden muss.

Viele Menschen haben jedoch schnell begonnen, Optionals einzuführen. Was ist der Vorteil der Verwendung von Optionen gegenüber einer Nullprüfung?

William Dunne
quelle
4
Sie möchten isPresent so gut wie nie verwenden, normalerweise sollten Sie ifPresent oder map
jk verwenden.
6
@jk. Weil ifAussagen im letzten Jahrzehnt soooooooooooooooooooooo sooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
user253751
2
@immibis Ich hatte einmal eine lange Debatte über dieses Thema mit einem anderen Benutzer. Der Punkt ist, dass if(x.isPresent) fails_on_null(x.get)Sie das Typensystem verlassen und die Garantie behalten müssen, dass der Code über die (zugegebenermaßen kurze) Distanz zwischen der Bedingung und dem Funktionsaufruf nicht "in Ihrem Kopf" zerbricht. Im optional.ifPresent(fails_on_null)Typensystem macht sich diese Garantie für Sie bezahlt, und Sie müssen sich keine Sorgen machen.
Ziggystar
1
Der Hauptfehler in Java bei der Verwendung Optional.ifPresent(und verschiedenen anderen Java-Konstrukten) besteht darin, dass Sie nur endgültige Variablen effektiv ändern und keine geprüften Ausnahmen auslösen können. Grund genug ifPresent, dies leider oft zu vermeiden .
Mike

Antworten:

19

Optionalnutzt das Typensystem für die Arbeit, die Sie sonst in Ihrem Kopf erledigen müssten: Erinnern Sie sich, ob eine bestimmte Referenz vorhanden ist oder nicht null. Das ist gut. Es ist immer klug, den Compiler mit langweiliger Drogenarbeit umgehen zu lassen und menschliche Gedanken für kreative, interessante Arbeit zu reservieren.

Ohne Optionalist jeder Verweis in Ihrem Code wie eine nicht explodierte Bombe. Der Zugriff darauf kann nützlich sein oder Ihr Programm mit einer Ausnahme beenden.

Mit Optional und ohne nullist jeder Zugriff auf eine normale Referenz erfolgreich, und jede Referenz auf eine Option ist erfolgreich, sofern sie nicht deaktiviert ist und Sie dies nicht überprüft haben. Das ist ein enormer Gewinn an Wartbarkeit.

Leider sind die meisten Sprachen, die jetzt angeboten Optionalwerden, nicht abgeschafft worden null. Sie können also nur von dem Konzept profitieren, indem Sie eine strikte Politik des "absolut Nein null, niemals" einführen. Daher ist OptionalJava beispielsweise nicht so überzeugend, wie es im Idealfall sein sollte.

Kilian Foth
quelle
Da Ihre Antwort die beste ist, kann es sich lohnen, die Verwendung von Lambdas als Vorteil hinzuzufügen - für jeden, der dies in Zukunft liest (siehe meine Antwort)
William Dunne
Die meisten modernen Sprachen bieten nicht nullfähige Typen, Kotlin, Typescript und AFAIK.
Zen
5
IMO ist dies besser mit einer Annotation @Nullable behandelt. Kein Grund, um Sie Optionalnur daran zu erinnern, dass der Wert null sein könnte
Hilikus
18

Ein Optionalbringt stärkere Typisierung in Operationen , die fehlschlagen, da die anderen Antworten werden abgedeckt, aber das ist weit von der interessanteste oder wertvoller Sache Optionalsan den Tisch bringen. Viel nützlicher ist die Möglichkeit, die Überprüfung auf Fehler zu verzögern oder zu vermeiden und viele möglicherweise fehlgeschlagene Vorgänge auf einfache Weise zusammenzustellen.

Überlegen Sie, ob Ihre optionalVariable aus Ihrem Beispielcode stammt, und führen Sie zwei zusätzliche Schritte aus, bei denen jeder möglicherweise fehlschlägt. Wenn ein Schritt auf dem Weg fehlschlägt, möchten Sie stattdessen einen Standardwert zurückgeben. Bei Optionalskorrekter Verwendung erhalten Sie Folgendes:

return optional.flatMap(x -> x.anotherOptionalStep())
               .flatMap(x -> x.yetAnotherOptionalStep())
               .orElse(defaultValue);

Bei nullhätte ich dreimal nullnachsehen müssen, bevor ich fortfahre, was dem Code eine Menge Komplexität und Wartungsprobleme hinzufügt. Optionalshabe diesen check in die flatMapund orElsefunktionen eingebaut .

Hinweis Ich habe nicht isPresenteinmal angerufen , was Sie bei der Verwendung als Code-Geruch betrachten sollten Optionals. Das bedeutet nicht unbedingt, dass Sie niemals verwenden sollten, sondernisPresent nur, dass Sie jeden Code, der dies tut, einer eingehenden Prüfung unterziehen sollten, um festzustellen, ob es einen besseren Weg gibt. Ansonsten haben Sie recht, Sie erhalten nur einen geringfügigen Sicherheitsvorteil gegenüber der Verwendung null.

Beachten Sie auch, dass ich nicht so besorgt bin, dies alles in eine Funktion zu kapseln, um andere Teile meines Codes vor Nullzeigern vor Zwischenergebnissen zu schützen. Wenn es .orElse(defaultValue)zum Beispiel sinnvoller ist, meine in einer anderen Funktion zu haben, habe ich viel weniger Bedenken, sie dort abzulegen, und es ist viel einfacher, die Operationen nach Bedarf zwischen verschiedenen Funktionen zusammenzustellen.

Karl Bielefeldt
quelle
10

Es wird die Möglichkeit von Null als gültige Antwort hervorgehoben, von der häufig (zu Recht oder zu Unrecht) angenommen wird, dass sie nicht zurückgegeben wird. Durch das Hervorheben der wenigen Male, wenn Null gültig ist, wird vermieden, dass Ihr Code mit einer großen Anzahl sinnloser Nullprüfungen verunreinigt wird.

Idealerweise würde das Setzen einer Variablen auf Null irgendwo anders als optional zu einem Fehler bei der Kompilierung führen. Beseitigen von Laufzeit-Nullzeiger-Ausnahmen. Offensichtlich schließt die Abwärtskompatibilität dies leider aus

Richard Tingle
quelle
4

Lassen Sie mich Ihnen ein Beispiel geben:

class UserRepository {
     User findById(long id);
}

Es ist ziemlich klar, wofür diese Methode gedacht ist. Aber was passiert, wenn das Repository keinen Benutzer mit der angegebenen ID enthält? Es könnte eine Ausnahme auslösen oder es gibt möglicherweise null zurück?

Zweites Beispiel:

class UserRepository {
     Optional<User> findById(long id);
}

Was passiert hier, wenn kein Benutzer mit der angegebenen ID vorhanden ist? Richtig, Sie erhalten die Instanz Optional.absent () und können sie mit Optional.isPresent () überprüfen. Die Methodensignatur mit Optional bezieht sich expliziter auf den Vertrag. Es ist sofort klar, wie sich die Methode verhalten soll.

Es hat auch einen Vorteil beim Lesen des Client-Codes:

User user = userRepository.findById(userId).get();

Ich habe mein Auge darauf trainiert, .get () als sofortigen Codegeruch zu sehen. Dies bedeutet, dass der Client den Vertrag der aufgerufenen Methode absichtlich ignoriert. Dies ist viel einfacher zu sehen als ohne Optional, da Sie in diesem Fall nicht sofort wissen, welchen Vertrag die aufgerufene Methode hat (ob sie in ihrer Rückgabe null zulässt oder nicht), sodass Sie diese zuerst überprüfen müssen.

Optional wird mit der Konvention verwendet, dass Methoden mit dem Rückgabetyp Optional niemals null zurückgeben, und normalerweise auch mit der (weniger starken) Konvention, dass Methoden ohne den optionalen Rückgabetyp niemals null zurückgeben (es ist jedoch besser, sie mit @Nonnull, @NotNull oder ähnlich zu kommentieren). .

qbd
quelle
3

Ein Optional<T>erlaubt Ihnen, "Fehler" oder "kein Ergebnis" als gültigen Antwort- / Rückgabewert für Ihre Methoden zu haben (denken Sie zB an eine Datenbanksuche). Die Verwendung von an Optionalanstelle von, nullum einen Fehler anzuzeigen / kein Ergebnis hat einige Vorteile:

  • Er kommuniziert deutlich , dass „Fehler“ ist eine Option. Der Benutzer Ihrer Methode muss nicht raten, ob nulleine Rückgabe möglich ist.
  • Der Wert nullsollte, zumindest meiner Meinung nach, nicht dazu verwendet werden, etwas anzuzeigen, da die Semantik möglicherweise nicht vollständig klar ist. Es sollte verwendet werden, um dem Garbage Collector mitzuteilen, dass das zuvor referenzierte Objekt gesammelt werden kann.
  • Die OptionalKlasse bietet viele nützliche Methoden, um mit den Werten bedingt zu arbeiten (z. B. ifPresent()) oder Standardwerte auf transparente Weise zu definieren ( orElse()).

Leider muss nullder Code nicht vollständig überprüft werden, da das OptionalObjekt selbst möglicherweise noch vorhanden ist null.

Pfannen
quelle
4
Dies ist nicht wirklich das, wofür Optional ist. Verwenden Sie eine Ausnahme, um einen Fehler zu melden. Wenn Sie das nicht können, verwenden Sie einen Typ, der entweder ein Ergebnis oder einen Fehlercode enthält .
James Youngman
Ich stimme überhaupt nicht zu. Optional.empty () ist meiner Meinung nach eine großartige Möglichkeit, Fehler oder Nichtexistenz anzuzeigen.
LegendLength
@LegendLength, ich muss im Falle eines Ausfalls strikt widersprechen. Wenn eine Funktion fehlschlägt, ist es besser, mir eine Erklärung zu geben, nicht nur ein leeres "Ich habe versagt"
Winston Ewert
Tut mir leid, ich stimme zu, ich meine "erwarteter Fehler", da ich bei einer Suche keinen Mitarbeiter mit dem Namen "x" gefunden habe.
LegendLength
3

Zusätzlich zu den anderen Antworten besteht der andere Hauptvorteil von Optionals beim Schreiben von sauberem Code darin, dass Sie einen Lambda-Ausdruck verwenden können, falls der Wert vorhanden ist, wie unten gezeigt:

opTr.ifPresent(tr -> transactions.put(tr.getTxid(), tr));
William Dunne
quelle
2

Betrachten Sie die folgende Methode:

void dance(Person partner)

Schauen wir uns nun einen Aufrufcode an:

dance(null);

Dies führt zu einem möglicherweise schwer zu verfolgenden Absturz, da dancekeine Null erwartet wurde.

dance(optionalPerson)

Dies führt zu einem Kompilierungsfehler, da dance ein Personnicht erwartet hatOptional<Person>

dance(optionalPerson.get())

Wenn ich keine Person hatte, führt dies zu einer Ausnahme in dieser Zeile, an dem Punkt, an dem ich unrechtmäßig versucht habe, die Optional<Person>in eine Person umzuwandeln . Der Schlüssel ist, dass anders als bei der Übergabe einer Null die Spuren außer hier nicht an irgendeiner anderen Stelle im Programm sind.

Zusammenfassend lässt sich sagen, dass die missbräuchliche Verwendung von optionalen Elementen das Auffinden von Problemen erleichtert.

Winston Ewert
quelle
1
Wenn Sie beim Aufrufen Optional.get()Ihres Codes Ausnahmen auslösen, ist Ihr Code schlecht geschrieben - Sie sollten fast nie Optional.get aufrufen, ohne Optional.isPresent()vorher zu prüfen (wie im OP angegeben), oder Sie könnten schreiben optionalPersion.ifPresent(myObj::dance).
Chris Cooper
@QmunkE, ja, Sie sollten Optional.get () nur aufrufen, wenn Sie bereits wissen, dass optional nicht leer ist (entweder weil Sie isPresent aufgerufen haben oder weil Ihr Algorithmus dies garantiert). Ich mache mir jedoch Sorgen darüber, dass Leute blind isPresent überprüfen und dann die fehlenden Werte ignorieren, was zu einer Vielzahl stiller Fehler führt, die sich als lästig erweisen.
Winston Ewert
1
Ich mag optionals. Aber ich werde sagen, dass einfache alte Nullzeiger im Code der realen Welt sehr selten ein Problem sind. Sie versagen fast immer in der Nähe der fehlerhaften Codezeile. Und ich denke, das wird von Leuten überstrapaziert, wenn sie über dieses Thema sprechen.
LegendLength
@LegendLength, meine Erfahrung ist, dass 95% der Fälle tatsächlich leicht nachvollziehbar sind, um festzustellen, woher die NULL stammt. Die verbleibenden 5% sind jedoch äußerst schwer aufzuspüren.
Winston Ewert
-1

In Objective-C sind alle Objektreferenzen optional. Aus diesem Grund ist Code wie Sie dumm.

if (variable != nil) {
    [variable doThing];
}

Code wie der obige ist in Objective-C unbekannt. Stattdessen würde der Programmierer einfach:

[variable doThing];

Wenn variableein Wert enthalten ist, doThingwird er aufgerufen. Wenn nicht, kein Schaden. Das Programm behandelt es als No-Op und läuft weiter.

Dies ist der eigentliche Vorteil von Optionals. Sie müssen Ihren Code nicht mit verunreinigen if (obj != nil).

Daniel T.
quelle
Ist das nicht nur eine Form des stillen Versagens? In einigen Fällen ist es Ihnen vielleicht wichtig, dass der Wert null ist, und Sie möchten etwas dagegen unternehmen.
LegendLength
-3

if (optional.isPresent ()) {

Das offensichtliche Problem hier ist , dass , wenn „optional“ wirklich ist , fehlt (dh null ist) , dann Ihr Code mit einer Nullreferenceexception sprengen wird (oder ähnlich) versucht , auf einer Null - Objekt Referenz jede Methode zu nennen!

Möglicherweise möchten Sie eine statische Helper-Klassenmethode schreiben, mit der die Nullheit eines bestimmten Objekttyps wie folgt bestimmt wird:

function isNull( object o ) 
{
   return ( null == o ); 
}

if ( isNull( optional ) ) ... 

oder vielleicht sinnvoller:

function isNotNull( object o ) 
{
   return ( null != o ); 
}

if ( isNotNull( optional ) ) ... 

Aber sind beide wirklich lesbarer / verständlicher / wartbarer als das Original?

if ( null == optional ) ... 
if ( null != optional ) ... 
Phill W.
quelle