Wir sind kürzlich zu Java 8 übergegangen. Jetzt sehe ich Anwendungen, die mit Optional
Objekten überflutet sind.
Vor Java 8 (Style 1)
Employee employee = employeeServive.getEmployee();
if(employee!=null){
System.out.println(employee.getId());
}
Nach Java 8 (Style 2)
Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
Employee employee = employeeOptional.get();
System.out.println(employee.getId());
}
Ich sehe keinen Mehrwert, Optional<Employee> employeeOptional = employeeService.getEmployee();
wenn der Dienst selbst optional zurückgibt:
Ich komme aus Java 6 und sehe Stil 1 klarer und mit weniger Codezeilen. Gibt es einen wirklichen Vorteil, den ich hier vermisse?
Konsolidiert aus dem Verständnis aller Antworten und weiteren Recherchen im Blog
employeeOptional.isPresent()
scheint der Punkt der Optionstypen völlig zu fehlen. Laut @ MikePartridges Kommentar ist dies weder empfohlen noch idiomatisch. Das explizite Überprüfen, ob ein Optionstyp etwas oder nichts ist, ist ein Nein-Nein. Sie sollen einfachmap
oderflatMap
über sie.Optional
von Brian Goetz , dem Java Language Architect bei Oracle.Antworten:
Style 2 ist für Java 8 nicht ausreichend, um den vollen Nutzen zu erzielen. Du willst das
if ... use
überhaupt nicht. Siehe Oracle Beispiele . Wenn wir ihren Rat befolgen, bekommen wir:Stil 3
Oder ein längerer Ausschnitt
quelle
void ifPresent(Consumer<T>, Runnable)
gäbe, würde ich für ein vollständiges Verbot vonisPresent
undget
ifPresentOrElse(Consumer<? super T>, Runnable)
.Wenn Sie
Optional
als "Kompatibilitätsebene" eine ältere API verwenden, die möglicherweise noch zurückgegeben wirdnull
, kann es hilfreich sein, die Option (nicht leer) zu erstellen, wenn Sie sich sicher sind , dass Sie über etwas verfügen. ZB wo du geschrieben hast:Ich würde mich entscheiden für:
Der Punkt ist, dass Sie wissen, dass es einen Nicht-Null-Angestellten- Service gibt , so dass Sie das in einem
Optional
mit einschließen könnenOptional.of()
. Wenn Sie darauf zurückgreifengetEmployee()
, erhalten Sie möglicherweise einen Mitarbeiter oder nicht. Dieser Mitarbeiter hat möglicherweise (oder möglicherweise auch nicht) einen Ausweis. Wenn Sie dann eine ID erhalten haben, möchten Sie diese ausdrucken.Es gibt keine Notwendigkeit, explizit für überprüft jeden null, Präsenz, usw., in diesem Code.
quelle
(...).ifPresent(aMethod)
overif((...).isPresent()) { aMethod(...); }
? Es scheint einfach noch eine andere Syntax zu sein, um fast die gleiche Operation auszuführen.Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet);
Jetzt kann ichchildren
zchildren.forEach(relative::sendGift);
. B. einheitlich verarbeiten . Die könnten auch kombiniert werden:Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet).forEach(relative::sendGift);
. Alle die Boilerplate der Nullprüfung usw.Es ist so gut wie kein Mehrwert,
Optional
einen einzigen Wert zu haben. Wie Sie gesehen haben, ersetzt dies nur einen Scheck aufnull
durch einen Scheck auf Anwesenheit.Es ist ein enormer Mehrwert,
Collection
solche Dinge zu haben. Alle Streaming-Methoden, die eingeführt wurden, um Low-Level-Loops alten Stils zu ersetzen, verstehen dieOptional
Dinge und machen das Richtige daraus (sie werden nicht verarbeitet oder geben letztendlich eine andere nicht gesetzte zurückOptional
). Wenn Sie sich häufiger mit n Elementen beschäftigen als mit einzelnen Elementen (und 99% aller realistischen Programme), ist es eine enorme Verbesserung, über eine integrierte Unterstützung für optional vorhandene Elemente zu verfügen. Im Idealfall müssen Sie nie nachsehennull
oder anwesend sein.quelle
Employee getEmployee()
vs.Optional<Employee> getEmployee()
Während technisch beide zurückkehren könntennull
, ist es weitaus wahrscheinlicher, dass einer von ihnen Probleme verursacht..map()
ohne auf dem ganzen Weg nach null suchen zu müssen, ist großartig. ZBOptional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);
.null
.Solange Sie
Optional
wie eine ausgefallene API für verwendenisNotNull()
, werden Sie keine Unterschiede feststellen, wenn Sie nur nachsehennull
.Dinge , die Sie sollten nicht verwenden
Optional
fürMit
Optional
nur auf das Vorhandensein eines Wertes zu überprüfen , ist Bad - Code ™:Dinge , die Sie verwenden sollten
Optional
fürVermeiden Sie, dass ein Wert nicht vorhanden ist, indem Sie bei Verwendung von null-zurückgegebenen API-Methoden bei Bedarf einen erstellen:
Oder wenn das Erstellen eines neuen Mitarbeiters zu kostspielig ist:
Machen Sie außerdem alle darauf aufmerksam, dass Ihre Methoden möglicherweise keinen Wert zurückgeben (falls Sie aus irgendeinem Grund keinen Standardwert wie oben zurückgeben können):
Und schließlich gibt es all Stream-ähnliche Methoden , die bereits in anderen Antworten erklärt worden, obwohl diejenigen , die nicht wirklich sind Sie entscheiden , zu verwenden ,
Optional
aber die Verwendung der HerstellungOptional
von jemandem anderen zur Verfügung gestellt.quelle
Optional
eine verpasste Gelegenheit. "Hier, haben Sie dieses neueOptional
Ding, das ich gemacht habe, damit Sie gezwungen sind, nach Nullwerten zu suchen. Oh, und übrigens ... Ich habe eineget()
Methode hinzugefügt , damit Sie nicht wirklich nach irgendetwas suchen müssen;);)" . (...)Optional
ist schön als "DIESES KÖNNTE NULL SEIN" -Neonschild, aber die bloße Existenz seinerget()
Methode lähmt es » . Aber OP fragte nach Gründen für die VerwendungOptional
, nicht nach Gründen dagegen oder nach Konstruktionsfehlern.Employee employee = Optional.ofNullable(employeeService.getEmployee());
Sollte der Typ in Ihrem dritten Snippet nicht so seinOptional<Employee>
?get
ist, wenn Sie mit altem Code interagieren müssen. Ich kann mir nicht viele triftige Gründe für die Verwendung von neuem Code vorstellenget
. Das heißt, wenn man mit altem Code interagiert, gibt es oft keine Alternative, aber walen hat den Punkt, dass die Existenzget
Anfängern das Schreiben von schlechtem Code verursacht.Map
einmal erwähnt und mir ist nicht bekannt, dass jemand eine so drastische, nicht abwärtskompatible Änderung vornimmt. Sie können also absichtlich schlechte APIs entwickeln,Optional
ohne sicher zu sein, was dies beweist. Eine MethodeOptional
wäre allerdings sinnvolltryGet
.Map
ist ein Beispiel, mit dem jeder vertraut ist. Natürlich können sie ausMap.get
Gründen der Abwärtskompatibilität kein Optional zurückgeben, aber Sie können sich leicht eine andere Map-ähnliche Klasse vorstellen, für die solche Kompatibilitätsbeschränkungen nicht gelten. Sprichclass UserRepository {Optional<User> getUserDetails(int userID) {...}}
. Jetzt möchten Sie die Details des angemeldeten Benutzers abrufen, und Sie wissen, dass diese vorhanden sind, da sie sich anmelden konnten und Ihr System niemals Benutzer löscht. Also tust du estheUserRepository.getUserDetails(loggedInUserID).get()
.Der entscheidende Punkt für mich bei der Verwendung von Optional ist, dass klar bleibt, wann der Entwickler überprüfen muss, ob die Funktion einen Wert zurückgibt oder nicht.
quelle
Ihr Hauptfehler ist, dass Sie immer noch prozeduraler denken. Dies ist nicht als Kritik an Ihnen als Person gedacht, sondern lediglich eine Beobachtung. Funktionaleres Denken ist mit Zeit und Übung verbunden, und daher sind die Methoden „Präsentieren“ und „Schauen“, wie die offensichtlichsten richtigen Dinge aussehen, die für Sie in Frage kommen. Ihr zweiter kleiner Fehler ist das Erstellen Ihrer Option in Ihrer Methode. Die Option soll dokumentieren, dass etwas einen Wert zurückgeben kann oder nicht. Sie könnten nichts bekommen.
Dies hat dazu geführt, dass Sie perfekt lesbaren Code geschrieben haben, der absolut vernünftig erscheint, aber Sie wurden von den abscheulichen Zwillingsversuchern verführt, die get und isPresent sind.
Natürlich wird die Frage schnell: "Warum sind wir präsent und kommen auch dort an?"
Eine Sache, die viele Leute hier vermissen, ist, dass isPresent () ist, dass es nicht für neuen Code gemacht ist, der von Leuten geschrieben wurde, die voll an Bord sind und wissen, wie verdammt nützliche Lambdas sind und wer die funktionale Sache mag.
Es gibt uns jedoch ein paar (zwei) gute, großartige, glamouröse (?) Vorteile:
Der erste ist ziemlich einfach.
Stellen Sie sich vor, Sie haben eine API, die so aussieht:
Und Sie hatten eine Legacy-Klasse, die dies als solches verwendete (füllen Sie die Lücken aus):
Ein erfundenes Beispiel, um sicher zu sein. Aber ertrage es hier mit mir.
Java 8 wurde nun gestartet und wir bemühen uns, an Bord zu kommen. Eines der Dinge, die wir tun, ist, dass wir unsere alte Schnittstelle durch etwas ersetzen möchten, das Optional zurückgibt. Warum? Denn wie jemand anderes schon gnädig gesagt hat: Dies nimmt die Vermutung auf sich, ob etwas null sein kann oder nicht. Dies wurde bereits von anderen hervorgehoben. Aber jetzt haben wir ein Problem. Stellen Sie sich vor, wir haben (entschuldigen Sie, während ich bei einer unschuldigen Methode Alt + F7 drücke) 46 Stellen, an denen diese Methode in gut getestetem Legacy-Code aufgerufen wird, der ansonsten hervorragende Arbeit leistet. Jetzt müssen Sie alle aktualisieren.
Hier erstrahlt isPresent.
Denn jetzt: Snickers lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {wirf eine neue NoSuchSnickersException (); } else {consumer.giveDiabetes (lastSnickers); }
wird:
Und dies ist eine einfache Änderung, die Sie dem neuen Junior geben können: Er kann etwas Nützliches tun und gleichzeitig die Codebasis erkunden. Win-Win. Immerhin ist so etwas ziemlich verbreitet. Und jetzt müssen Sie den Code nicht mehr umschreiben, um Lambdas oder ähnliches zu verwenden. (In diesem speziellen Fall wäre es trivial, aber ich überlasse es dem Leser, sich Beispiele auszudenken, bei denen es als Übung schwierig wäre.)
Beachten Sie, dass dies bedeutet, dass die Art und Weise, wie Sie dies getan haben, im Wesentlichen ein Weg ist, mit Legacy-Code umzugehen, ohne kostspielige Umschreibungen vorzunehmen. Was ist mit neuem Code?
Nun, in Ihrem Fall, in dem Sie nur etwas ausdrucken möchten, tun Sie einfach Folgendes:
snickersCounter.lastConsumedSnickers (). ifPresent (System.out :: println);
Das ist ziemlich einfach und vollkommen klar. Der Punkt, der dann langsam an die Oberfläche sprudelt, ist, dass es Use Cases für get () und isPresent () gibt. Mit ihnen können Sie vorhandenen Code mechanisch ändern, um die neueren Typen zu verwenden, ohne zu viel darüber nachzudenken. Was Sie tun, ist daher auf folgende Weise falsch:
Wenn Sie Optional als einfache Null-Sicherheitsüberprüfung verwenden möchten, sollten Sie Folgendes tun:
Natürlich sieht die gut aussehende Version so aus:
Übrigens, obwohl es keinesfalls erforderlich ist, empfehle ich, pro Operation eine neue Zeile zu verwenden, damit es einfacher zu lesen ist. Leicht zu lesende und verständliche Beats prägnant an jedem Wochentag.
Dies ist natürlich ein sehr einfaches Beispiel, in dem es einfach ist, alles zu verstehen, was wir versuchen zu tun. Im wirklichen Leben ist es nicht immer so einfach. Beachten Sie jedoch, dass wir in diesem Beispiel unsere Absichten zum Ausdruck bringen. Wir möchten den Mitarbeiter abrufen, seinen Ausweis abrufen und ihn, falls möglich, ausdrucken. Dies ist der zweite große Gewinn mit Optional. Dadurch können wir klareren Code erstellen. Ich denke auch, dass es im Allgemeinen eine gute Idee ist, Dinge wie die Erstellung einer Methode zu tun, die eine Reihe von Dingen ausführt, damit Sie sie einer Karte hinzufügen können.
quelle
map(x).ifPresent(f)
macht einfach nicht den gleichen intuitiven Sinn, den manif(o != null) f(x)
hat. Dieif
Version ist völlig klar. Dies ist ein weiterer Fall von Leuten, die auf einem beliebten Musikwagen springen, * kein * Fall von wirklichem Fortschritt.Optional
. Ich bezog mich nur auf die Verwendung von optional in diesem speziellen Fall und mehr noch auf die Lesbarkeit, da mehrere Personen so handeln, dass dieses Format gleich oder besser lesbar ist alsif
.isPresent
undget
so viel wie möglich realistisch sind) die Sicherheit verbessern . Es ist schwieriger, falschen Code zu schreiben, der das Fehlen eines Werts nicht überprüft, wenn der TypOptional<Whatever>
keine Nullable verwendetWhatever
.get
, sind Sie gezwungen , zu entscheiden , was zu tun ist, wenn ein fehlender Wert auftritt: Sie würden entweder verwendenorElse()
(oderorElseGet()
) eine Standard zu liefern, oderorElseThrow()
ein werfen gewählt Ausnahme, die die Art des Problems dokumentiert. Dies ist besser als eine generische NPE, die bedeutungslos ist, bis Sie in die Stapelablaufverfolgung eingreifen.Ich sehe keinen Vorteil in Stil 2. Sie müssen immer noch erkennen, dass Sie die Nullprüfung benötigen, und jetzt ist sie noch größer und daher weniger lesbar.
Ein besserer Stil wäre, wenn employeeServive.getEmployee () den Wert Optional zurückgibt und der Code dann wie folgt lautet:
Auf diese Weise können Sie "employeeServive.getEmployee (). GetId ()" nicht aufrufen, und Sie bemerken, dass Sie mit optionalem Wert irgendwie umgehen sollten. Und wenn in Ihrer gesamten Codebasis Methoden nie null zurückgeben (oder fast nie, wenn Ihrem Team Ausnahmen von dieser Regel bekannt sind), erhöht dies die Sicherheit gegen NPE.
quelle
isPresent()
. Wir verwenden optional, damit wir nicht testen müssen.isPresent()
. Dies ist der falsche Ansatz für Optionen. Verwenden Sie stattdessenmap
oderflatMap
.ifPresent
) nur eine andere Möglichkeit zum Testen.ifPresent(x)
ist genauso ein Test wieif(present) {x}
. Der Rückgabewert ist irrelevant.Ich denke, der größte Vorteil ist, dass wenn Ihre Methodensignatur eine zurückgibt,
Employee
Sie nicht auf Null prüfen. Sie wissen durch diese Unterschrift, dass die Rückgabe eines Mitarbeiters garantiert ist. Sie müssen keinen Fehlerfall behandeln. Mit einem optionalen wissen Sie, was Sie tun.In Code, den ich gesehen habe, gibt es Nullprüfungen, die niemals fehlschlagen werden, da die Benutzer den Code nicht nachverfolgen möchten, um festzustellen, ob eine Null möglich ist, sodass sie überall defensiv Nullprüfungen durchführen. Das macht den Code etwas langsamer, aber was noch wichtiger ist, es macht den Code viel lauter.
Damit dies funktioniert, müssen Sie dieses Muster jedoch konsistent anwenden.
quelle
@Nullable
und@ReturnValuesAreNonnullByDefault
zusammen mit dem statischen Analyse.package-info.java
mit@ReturnValuesAreNonnullByDefault
allen meinen Paketen, so alles , was ich tun müssen, ist hinzuzufügen ,@Nullable
wo Sie wechseln müssenOptional
. Dies bedeutet weniger Zeichen, keinen zusätzlichen Müll und keinen Laufzeitaufwand. Ich muss die Dokumentation nicht nachschlagen, da sie beim Bewegen des Mauszeigers über die Methode angezeigt wird. Das statische Analysetool erkennt Fehler und spätere Änderungen der Nichtigkeit.Optional
ist, dass es eine alternative Möglichkeit zum Umgang mit Nullability bietet. Wenn es von Anfang an in Java war, mag es in Ordnung sein, aber jetzt ist es nur ein Schmerz. Den Kotlin-Weg zu gehen, wäre meiner Meinung nach viel besser. +++ Beachten Sie, dass diesList<@Nullable String>
immer noch eine Liste von Zeichenfolgen ist, während diesList<Optional<String>>
nicht der Fall ist.Meine Antwort lautet: Tu es einfach nicht. Überlegen Sie sich mindestens zweimal, ob es sich wirklich um eine Verbesserung handelt.
Ein Ausdruck (entnommen aus einer anderen Antwort) wie
scheint die richtige funktionale Art zu sein, mit ursprünglich nullfähigen Rückgabemethoden umzugehen. Es sieht gut aus, wirft nie und bringt Sie nie dazu, über die Behandlung des "abwesenden" Falls nachzudenken.
Es sieht besser aus als im alten Stil
das macht es offensichtlich, dass es
else
Klauseln geben kann .Der funktionale Stil
orElse
nicht immer aus)In keinem Fall sollte es als Allheilmittel verwendet werden. Wenn es universell anwendbar war, sollte die Semantik des
.
Operators durch die Semantik des?.
Operators ersetzt , die NPE vergessen und alle Probleme ignoriert werden.Obwohl ich ein großer Java-Fan bin, muss ich sagen, dass der funktionale Stil nur ein Ersatz für die Aussage eines armen Java-Mannes ist
bekannt aus anderen Sprachen. Stimmen wir dieser Aussage zu?
quelle
employeeService.getEmployee().getId().apply(id => System.out.println(id));
und machen Sie es einmal mit dem Operator "Sichere Navigation" und einmal mit dem Operator "Sicher" nullsicherOptional
. Sicher, wir könnten es ein "Implementierungsdetail" nennen, aber die Implementierung ist wichtig. Es gibt Fälle, in denen Lamdas Code einfacher und schöner machen, aber hier ist es nur ein hässlicher Missbrauch.Optional.of(employeeService).map(EmployeeService::getEmployee).map(Employee::getId).ifPresent(System.out::println);
Sprache - Funktion:employeeService.getEmployee()?.getId()?.apply(id => System.out.println(id));
. Zwei Syntaxen, aber sie sehen mir am Ende sehr ähnlich . Und das "Konzept" istOptional
akaNullable
akaMaybe
+++
Was missbraucht wird, sind Lamdas für den Umgang mit Nullability. Lamdas sind in Ordnung, ist es aberOptional
nicht. Nullability benötigt einfach eine angemessene Sprachunterstützung wie in Kotlin. Ein weiteres Problem:List<Optional<String>>
hat nichts mitList<String>
dem zu tun, wo wir sie brauchen würden, um verwandt zu sein.Optional ist ein Konzept (ein Typ höherer Ordnung) aus der funktionalen Programmierung. Durch die Verwendung entfällt die Überprüfung auf verschachtelte Nullen und Sie werden vor der Pyramide des Untergangs bewahrt.
quelle