it-swarm.com.de

Warum sollte Optional in Java 8+) anstelle herkömmlicher Nullzeigerprüfungen verwendet werden?

Wir sind kürzlich zu Java 8.) übergegangen. Jetzt sehe ich Anwendungen, die mit Optional Objekten überflutet sind.

Vor Java 8 (Stil 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 von Optional<Employee> employeeOptional = employeeService.getEmployee();, wenn der Dienst selbst optional zurückgibt:

Ich komme aus einem Java 6 Hintergrund) 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 unter Blog

118
user3198603

Stil 2 geht nicht Java 8 genug, um den vollen Nutzen zu sehen. Sie wollen nicht das if ... use überhaupt. Siehe Oracle-Beispiele . Wenn wir ihren Rat befolgen, erhalten wir:

Stil 3

// Changed EmployeeServive to return an optional, no more nulls!
Optional<Employee> employee = employeeServive.getEmployee();
employee.ifPresent(e -> System.out.println(e.getId()));

Oder ein längerer Ausschnitt

Optional<Employee> employee = employeeServive.getEmployee();
// Sometimes an Employee has forgotten to write an up-to-date timesheet
Optional<Timesheet> timesheet = employee.flatMap(Employee::askForCurrentTimesheet); 
// We don't want to do the heavyweight action of creating a new estimate if it will just be discarded
client.bill(timesheet.orElseGet(EstimatedTimesheet::new));
116
Caleth

Wenn Sie Optional als "Kompatibilitäts" -Ebene zwischen einer älteren API verwenden, die möglicherweise noch null zurückgibt, kann es hilfreich sein, die (nicht leere) Option spätestens zu diesem Zeitpunkt zu erstellen du bist sicher dass du etwas hast. Zum Beispiel, wo Sie geschrieben haben:

Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

Ich würde mich entscheiden für:

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

Der Punkt ist, dass Sie wissen, dass es einen Nicht-Null-Mitarbeiter gibt Service, so dass Sie dies in einem Optional mit Optional.of() zusammenfassen können. . Wenn Sie dann getEmployee() anrufen, erhalten Sie möglicherweise einen Mitarbeiter oder nicht. Dieser Mitarbeiter kann (oder möglicherweise auch nicht) einen Ausweis haben. Wenn Sie dann eine ID erhalten haben, möchten Sie diese ausdrucken.

In diesem Code muss nicht explizit nach any null, Präsenz usw. gesucht werden.

48
Joshua Taylor

Es gibt so gut wie keinen Mehrwert, ein Optional eines einzelnen Wertes zu haben. Wie Sie gesehen haben, ersetzt dies lediglich eine Prüfung auf null durch eine Prüfung auf Anwesenheit.

Es ist riesig Mehrwert, ein Collection solcher Dinge zu haben. Alle Streaming-Methoden, die eingeführt wurden, um Low-Level-Schleifen alten Stils zu ersetzen, verstehen Optional Dinge und tun das Richtige dagegen (sie nicht zu verarbeiten oder letztendlich ein anderes nicht gesetztes Optional zurückzugeben). Wenn Sie sich häufiger mit n Elementen befassen als mit einzelnen Elementen (und 99% aller realistischen Programme), ist es eine enorme Verbesserung, diese erstellt zu haben -in Unterstützung für optional präsentierte Dinge. Im Idealfall müssen Sie nie nach null oder Präsenz suchen.

23
Kilian Foth

Solange Sie Optional wie eine ausgefallene API für isNotNull() verwenden, werden Sie keine Unterschiede feststellen, wenn Sie nur nach null suchen.

Dinge, für die Sie NICHT Optional verwenden sollten

Die Verwendung von Optional, um nur das Vorhandensein eines Werts zu überprüfen, ist Bad Code ™:

// BAD CODE ™ --  just check getEmployee() != null  
Optional<Employee> employeeOptional =  Optional.ofNullable(employeeService.getEmployee());  
if(employeeOptional.isPresent()) {  
    Employee employee = employeeOptional.get();  
    System.out.println(employee.getId());
}  

Dinge, für die Sie Optional verwenden sollten

Vermeiden Sie, dass ein Wert nicht vorhanden ist, indem Sie bei Bedarf einen Wert erstellen, wenn Sie API-Methoden mit null Rückgabe verwenden:

Employee employee = Optional.ofNullable(employeeService.getEmployee())
                            .orElseGet(Employee::new);
System.out.println(employee.getId());

Oder wenn das Erstellen eines neuen Mitarbeiters zu kostspielig ist:

Optional<Employee> employee = Optional.ofNullable(employeeService.getEmployee());
System.out.println(employee.map(Employee::getId).orElse("No employee found"));

Machen Sie alle darauf aufmerksam, dass Ihre Methoden möglicherweise keinen Wert zurückgeben (wenn Sie aus irgendeinem Grund keinen Standardwert wie oben zurückgeben können):

// Your code without Optional
public Employee getEmployee() {
    return someCondition ? null : someEmployee;
}
// Someone else's code
Employee employee = getEmployee(); // compiler doesn't complain
// employee.get...() -> NPE awaiting to happen, devs criticizing your code

// Your code with Optional
public Optional<Employee> getEmployee() {
    return someCondition ? Optional.empty() : Optional.of(someEmployee);
}
// Someone else's code
Employee employee = getEmployee(); // compiler complains about incompatible types
// Now devs either declare Optional<Employee>, or just do employee = getEmployee().get(), but do so consciously -- not your fault.

Und schließlich gibt es all die Stream-ähnlichen Methoden, die bereits in anderen Antworten erklärt wurden, obwohl diese nicht wirklich Sie entscheiden, Optional zu verwenden, sondern das Optional von jemand anderem bereitgestellt.

18
walen

Der wichtigste Punkt für mich bei der Verwendung von Optional ist, klar zu bleiben, wenn Entwickler prüfen müssen, ob die Funktion einen Rückgabewert liefert oder nicht.

//service 1
Optional<Employee> employeeOptional = employeeService.getEmployee();
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

//service 2
Employee employee = employeeService.getEmployeeXTPO();
System.out.println(employee.getId());
12
Rodrigo Menezes

Ihr Hauptfehler ist, dass Sie immer noch prozeduraler denken. Dies ist nicht als Kritik an Ihnen als Person gedacht, sondern lediglich als Beobachtung. Das funktionalere Denken erfordert Zeit und Übung, und daher sind die Methoden vorhanden und sehen aus wie die offensichtlichsten richtigen Dinge, die Sie benötigen. Ihr zweiter kleiner Fehler ist das Erstellen Ihrer Option innerhalb Ihrer Methode. Die Option soll helfen, zu dokumentieren, dass etwas einen Wert zurückgeben kann oder nicht. Sie könnten nichts bekommen.

Dies hat dazu geführt, dass Sie zwar perfekt lesbaren Code schreiben, der durchaus vernünftig erscheint, aber Sie wurden von den abscheulichen Zwillingstempeln verführt, die get und isPresent sind.

Natürlich stellt sich schnell die Frage: "Warum sind isPresent und kommen auch dort hin?"

Eine Sache, die viele Leute hier vermissen, ist, dass isPresent () nicht für neuen Code gemacht ist, der von Leuten geschrieben wurde, die voll an Bord sind, wie verdammt nützlich Lambdas sind und die die funktionale Sache mögen.

Es gibt uns jedoch ein paar (zwei) gute, großartige, glamouröse (?) Vorteile:

  • Es erleichtert den Übergang von Legacy-Code zur Nutzung der neuen Funktionen.
  • Es erleichtert die Lernkurven von Optional.

Der erste ist ziemlich einfach.

Stellen Sie sich vor, Sie haben eine API, die so aussieht:

public interface SnickersCounter {
  /** 
   * Provides a proper count of how many snickers have been consumed in total.
   */
  public SnickersCount howManySnickersHaveBeenEaten();

  /**
    * returns the last snickers eaten.<br>
    * If no snickers have been eaten null is returned for contrived reasons.
    */
  public Snickers lastConsumedSnickers();
}

Und Sie hatten eine Legacy-Klasse, die dies als solche verwendete (füllen Sie die Lücken aus):

Snickers lastSnickers = snickersCounter.lastConsumedSnickers();
if(null == lastSnickers) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers);
}

Ein erfundenes Beispiel, um sicher zu sein. Aber ertrage es hier mit mir.

Java 8 ist jetzt 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 bereits gnädig erwähnt hat: Dies nimmt das Rätselraten, ob etwas null sein kann oder nicht Dies wurde bereits von anderen darauf hingewiesen. 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 diese aktualisieren.

Hier leuchtet isPresent.

Denn jetzt: Snickers lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {neue NoSuchSnickersException () auslösen; } else {consumer.giveDiabetes (lastSnickers); }}

wird:

Optional<Snickers> lastSnickers = snickersCounter.lastConsumedSnickers();
if(!lastSnickers.isPresent()) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers.get());
}

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 etwas Ähnliches wie dieses Muster ziemlich weit 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, Beispiele zu finden, bei denen es als Übung schwierig wäre.)

Beachten Sie, dass dies bedeutet, dass die Art und Weise, wie Sie es getan haben, im Wesentlichen eine Möglichkeit ist, mit Legacy-Code umzugehen, ohne kostspielige Umschreibungen vorzunehmen. Was ist also mit neuem Code?

In Ihrem Fall, in dem Sie nur etwas ausdrucken möchten, tun Sie einfach Folgendes:

snickersCounter.lastConsumedSnickers (). ifPresent (System.out :: println);

Welches ist ziemlich einfach und vollkommen klar. Der Punkt, der dann langsam an die Oberfläche sprudelt, ist, dass es Anwendungsfälle für get () und isPresent () gibt. Sie dienen dazu, vorhandenen Code mechanisch zu ändern, um die neueren Typen zu verwenden, ohne zu viel darüber nachzudenken. Was Sie tun, ist daher auf folgende Weise falsch:

  • Sie rufen eine Methode auf, die möglicherweise null zurückgibt. Die richtige Idee wäre, dass die Methode null zurückgibt.
  • Sie verwenden die alten Bandaid-Methoden, um mit dieser Option umzugehen, anstatt die leckeren neuen Methoden zu verwenden, die Lambda-Fantasie enthalten.

Wenn Sie Optional als einfache Null-Sicherheitsüberprüfung verwenden möchten, sollten Sie Folgendes tun:

new Optional.ofNullable(employeeServive.getEmployee())
    .map(Employee::getId)
    .ifPresent(System.out::println);

Natürlich sieht die gut aussehende Version so aus:

employeeService.getEmployee()
    .map(Employee::getId)
    .ifPresent(System.out::println);

Übrigens, obwohl dies keinesfalls erforderlich ist, empfehle ich die Verwendung einer neuen Zeile pro Operation, damit es leichter zu lesen ist. Leicht zu lesen und zu verstehen schlägt die Prägnanz an jedem Tag der Woche.

Dies ist natürlich ein sehr einfaches Beispiel, bei dem es einfach ist, alles zu verstehen, was wir versuchen zu tun. Im wirklichen Leben ist das nicht immer so einfach. Beachten Sie jedoch, dass wir in diesem Beispiel unsere Absichten zum Ausdruck bringen. Wir möchten den Mitarbeiter ERHALTEN, seine ID ERHALTEN und wenn möglich ausdrucken. Dies ist der zweite große Gewinn mit Optional. Es ermöglicht uns, klareren Code zu erstellen. Ich denke auch, dass es im Allgemeinen eine gute Idee ist, Dinge wie eine Methode zu erstellen, die eine Menge Dinge erledigt, damit Sie sie einer Karte zuführen können.

8
Haakon Løtveit

Meine Antwort lautet: Tu es einfach nicht. Überlegen Sie mindestens zweimal, ob es sich wirklich um eine Verbesserung handelt.

Ein Ausdruck (aus einer anderen Antwort entnommen) wie

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

scheint der richtige funktionale Weg zu sein, um mit ursprünglich nullbaren Rückgabemethoden umzugehen. Es sieht gut aus, es wirft nie und es bringt dich nie dazu, über den "abwesenden" Fall nachzudenken.

Es sieht besser aus als im alten Stil

Employee employee = employeeService.getEmployee();
if (employee != null) {
    ID id = employee.getId();
    if (id != null) {
        System.out.println(id);
    }
}

was es offensichtlich macht, dass es else Klauseln geben kann.

Der funktionale Stil

  • sieht völlig anders aus (und ist für Leute, die es nicht gewohnt sind, unlesbar)
  • ist ein Schmerz zu debuggen
  • kann nicht einfach erweitert werden, um den "abwesenden" Fall zu behandeln (orElse ist nicht immer ausreichend)
  • führt dazu, den "abwesenden" Fall zu ignorieren

In keinem Fall sollte es als Allheilmittel verwendet werden. Wenn es universell anwendbar war, sollte die Semantik des Operators . Ersetzt werden Durch die Semantik des Operators ?. wurde die NPE vergessen und alle Probleme ignoriert.


Als großer Java Fan muss ich sagen, dass der funktionale Stil nur der Ersatz der Aussage durch einen armen Java Mann ist

employeeService
.getEmployee()
?.getId()
?.apply(id => System.out.println(id));

aus anderen Sprachen bekannt. Stimmen wir dieser Aussage zu?

  • ist nicht (wirklich) funktionsfähig?
  • ist dem alten Code sehr ähnlich, nur viel einfacher?
  • ist viel besser lesbar als der funktionale Stil?
  • ist debuggerfreundlich?
0
maaartinus

Optional ist ein Konzept (ein Typ höherer Ordnung) aus der funktionalen Programmierung. Wenn Sie es verwenden, entfällt die Prüfung verschachtelter Nullen und Sie werden vor der Pyramide des Schicksals bewahrt.

0
Petras Purlys

Was würde passieren, wenn "employeeService" selbst null ist? In beiden Codefragmenten wird die Nullzeigerausnahme ausgelöst.

Es kann ohne Verwendung von optional behandelt werden als:

if(employeeServive != null) {
    Employee employee = employeeServive.getEmployee();
    if(employee!=null){
        System.out.println(employee.getId());
    }
}

Wie Sie jedoch sehen können, gibt es mehrere if-Prüfungen, und es kann zu einer langen Hierarchie mit einer langen verschachtelten Struktur wie employeeService.getEmployee (). GetDepartment (). GetName () .... getXXX (). GetYYY () usw. Erweitert werden.

Um diese Art von Szenarien zu behandeln, können wir die optionale Klasse wie folgt verwenden:

Optional.ofNullable(employeeService)
     .map(service -> service.getEmployee())
     .map(employee -> employee.getId())
     .ifPresent(System.out::println);

Und es kann jede Größe der Verschachtelung verarbeiten.

Um alles über Java Optional) zu erfahren, lesen Sie meinen Artikel zu diesem Thema: Optional in Java 8

0
Shubham Bansal

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 somit weniger lesbar.

Ein besserer Stil wäre, wenn employeeServive.getEmployee () Optional zurückgeben würde und der Code dann:

Optional<Employee> employeeOptional = employeeServive.getEmployee();
if(employeeOptional.isPresent()){
    Employee employee= employeeOptional.get();
    System.out.println(employee.getId());
}

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 niemals null zurückgegeben wird (oder fast nie mit bekannten Ausnahmen von dieser Regel), erhöht dies die Sicherheit gegen NPE.

0
user470365

Ich denke, der größte Vorteil ist, dass Sie nicht auf null prüfen, wenn Ihre Methodensignatur ein Employee zurückgibt. Durch diese Unterschrift wissen Sie, dass die Rückgabe eines Mitarbeiters garantiert ist. Sie müssen einen Fehlerfall nicht behandeln. Mit einem optionalen wissen Sie, dass Sie es tun.

In Code, den ich gesehen habe, gibt es Nullprüfungen, die niemals fehlschlagen werden, da die Leute den Code nicht nachverfolgen möchten, um festzustellen, ob eine Null möglich ist, und daher überall defensiv Nullprüfungen durchführen. Dies 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.

0
Sled