Warum sollte Java 8's Optional nicht in Argumenten verwendet werden?


392

Ich habe auf vielen Websites gelesen. Optional sollte nur als Rückgabetyp verwendet und nicht in Methodenargumenten verwendet werden. Ich kämpfe darum, einen logischen Grund dafür zu finden. Zum Beispiel habe ich eine Logik, die 2 optionale Parameter hat. Daher halte ich es für sinnvoll, meine Methodensignatur wie folgt zu schreiben (Lösung 1):

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {
    // my logic
}

Viele Webseiten geben an, dass Optional nicht als Methodenargumente verwendet werden sollte. In diesem Sinne könnte ich die folgende Methodensignatur verwenden und einen eindeutigen Javadoc-Kommentar hinzufügen, um anzugeben, dass die Argumente null sein können, in der Hoffnung, dass zukünftige Betreuer das Javadoc lesen und daher vor der Verwendung der Argumente immer Nullprüfungen durchführen (Lösung 2). ::

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

Alternativ könnte ich meine Methode durch vier öffentliche Methoden ersetzen, um eine schönere Schnittstelle bereitzustellen und klarer zu machen, dass p1 und p2 optional sind (Lösung 3):

public int calculateSomething() {
    calculateSomething(null, null);
}

public int calculateSomething(String p1) {
    calculateSomething(p1, null);
}

public int calculateSomething(BigDecimal p2) {
    calculateSomething(null, p2);
}

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

Jetzt versuche ich, den Code der Klasse zu schreiben, die diese Logik für jeden Ansatz aufruft. OptionalIch rufe zuerst die beiden Eingabeparameter von einem anderen Objekt ab, das s zurückgibt, und rufe dann auf calculateSomething. Wenn also Lösung 1 verwendet wird, würde der aufrufende Code folgendermaßen aussehen:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);

Wenn Lösung 2 verwendet wird, sieht der aufrufende Code folgendermaßen aus:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));

Wenn Lösung 3 angewendet wird, könnte ich den obigen Code verwenden oder den folgenden (aber es ist deutlich mehr Code):

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result;
if (p1.isPresent()) {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p1, p2);
    } else {
        result = myObject.calculateSomething(p1);
    }
} else {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p2);
    } else {
        result = myObject.calculateSomething();
    }
}

Meine Frage lautet also: Warum wird es als schlechte Praxis angesehen, Optionals als Methodenargumente zu verwenden (siehe Lösung 1)? Es scheint mir die am besten lesbare Lösung zu sein und macht deutlich, dass die Parameter für zukünftige Betreuer leer / null sein können. (Ich bin mir bewusst, dass die Designer Optionalbeabsichtigt haben, es nur als Rückgabetyp zu verwenden, aber ich kann keine logischen Gründe finden, es in diesem Szenario nicht zu verwenden.)


16
Wenn Sie Optionals verwendet haben, müssten Sie dann nicht überprüfen, ob das als Parameter übergebene Optional nicht vorhanden ist null?
Biziclop

17
Ja, aber es würde jemand anderem klar machen, dass der Parameter in Zukunft leer / null sein kann, sodass möglicherweise in Zukunft eine Nullzeigerausnahme vermieden wird
Neil Stevens

1
Beeindruckend. Nullargumente werden an eine Methode übergeben? Das ist so Visual Basic. Welches Prinzip verletzt es? SRP (vielleicht). Es verstößt auch gegen ein anderes Prinzip, dessen Name mich beraubt, indem es NUR die notwendigen Informationen weitergibt, damit eine Methode oder Funktion ihre Aufgabe erfüllen kann.
Leuchtend

20
Alles, was theoretisch möglicherweise null ist, ist wie jede Methode in einer Bibliothek, die möglicherweise System.exit (0) aufruft. Sie können nicht gegen dies prüfen und Sie sollten nicht dagegen prüfen. Alles, was Sie die ganze Zeit tun müssten, sollten Sie tatsächlich (fast) nie tun. Als ob alle Parameter endgültig wären. Mit Ihren Tools können Sie verhindern, dass sich Parameterwerte ändern oder das Initialisieren von Feldern vergessen, anstatt Ihren Code für tausend Endspiele und ebenso viele Nullprüfungen unlesbar zu machen.
Yeoman

6
Verwenden Sie eigentlich nur die Annotationen NonNull / Nullable. Dies ist das, wonach Sie in dieser Situation suchen, nicht optional.
Bill K

Antworten:


202

Oh, diese Codierungsstile sind mit etwas Salz zu nehmen.

  1. (+) Übergeben eines optionalen Ergebnisses an eine andere Methode ohne semantische Analyse; Das der Methode zu überlassen, ist in Ordnung.
  2. (-) Die Verwendung optionaler Parameter, die eine bedingte Logik innerhalb der Methoden verursachen, ist buchstäblich kontraproduktiv.
  3. (-) Die Notwendigkeit, ein Argument in eine Option zu packen, ist für den Compiler nicht optimal und führt einen unnötigen Umbruch durch.
  4. (-) Im Vergleich zu nullbaren Parametern ist Optional teurer.

Im Allgemeinen: Optional werden zwei Zustände vereinheitlicht, die entwirrt werden müssen. Daher besser geeignet für das Ergebnis als für die Eingabe, für die Komplexität des Datenflusses.


38
Eigentlich ist eine mit Optionalrepräsentiert Parameter einen von drei Zuständen: einem nullOptional, eine nicht-null Optionalmit isPresent() == falseund einem nicht-null Optionaleinen Istwert Einwickeln.
Biziclop

16
@biziclop ja, ein unvermeidlicher Punkt bereits kritisiert. Es ist jedoch beabsichtigt , nur Nicht-Null-Ausdrücke zu verwenden. Dass es nicht lange gedauert hat, den Rat zu hören, Optional in einigen Fällen auch zu vermeiden, ist ziemlich ironisch. A @NotNull Optional.
Joop Eggen

80
@biziclop Beachten Sie, dass, wenn Sie Optionalüberhaupt verwenden, Status 1 ( nulloptional) normalerweise einen Programmierfehler anzeigt, so dass Sie es genauso gut nicht behandeln können (lassen Sie es einfach ein NullPointerException)
user253751

50
"suboptimal für den Compiler", "Im Vergleich zu nullbaren Parametern Optional ist teurer" - diese Argumente könnten für die Sprache C und nicht für die Sprache Java gültig sein. Java-Programmierer sollten sich auf sauberen Code, Portabilität, Testbarkeit, gute Architektur, Modularität usw. konzentrieren und nicht auf "Optional ist teurer als Nullreferenz". Und wenn Sie feststellen, dass Sie sich auf Mikrooptimierungen konzentrieren müssen, sollten Sie Objekte, Listen, Generika überspringen und zu Arrays und Grundelementen wechseln (ich möchte hier nicht anstößig sein, ich teile nur meine Meinung). .
Kacper86

15
"suboptimal für den Compiler": Vorzeitige Optimierung ist die Wurzel allen Übels ... Wenn Sie keinen leistungskritischen Code schreiben (was häufig der Fall ist, wenn Sie versuchen, eine saubere Schnittstelle anzugeben), sollte dies kein Problem sein.
Mohan

165

Der beste Beitrag, den ich zu diesem Thema gesehen habe, wurde von Daniel Olszewski geschrieben :

Obwohl es verlockend sein könnte, Optional für nicht obligatorische Methodenparameter in Betracht zu ziehen, verblasst eine solche Lösung im Vergleich zu anderen möglichen Alternativen. Untersuchen Sie zur Veranschaulichung des Problems die folgende Konstruktordeklaration:

public SystemMessage(String title, String content, Optional<Attachment> attachment) {
    // assigning field values
}

Auf den ersten Blick mag es als richtige Designentscheidung erscheinen. Immerhin haben wir den Anhangsparameter explizit als optional markiert. Beim Aufrufen des Konstruktors kann der Client-Code jedoch etwas ungeschickt werden.

SystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty());
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));

Anstatt Klarheit zu schaffen, lenken die Factory-Methoden der optionalen Klasse den Leser nur ab. Beachten Sie, dass es nur einen optionalen Parameter gibt, aber stellen Sie sich zwei oder drei vor. Onkel Bob wäre definitiv nicht stolz auf einen solchen Code 😉

Wenn eine Methode optionale Parameter akzeptieren kann, ist es vorzuziehen, den bewährten Ansatz zu übernehmen und einen solchen Fall mithilfe von Methodenüberladung zu entwerfen. Im Beispiel der SystemMessage-Klasse ist die Deklaration von zwei separaten Konstruktoren der Verwendung von Optional überlegen.

public SystemMessage(String title, String content) {
    this(title, content, null);
}

public SystemMessage(String title, String content, Attachment attachment) {
    // assigning field values
}

Diese Änderung macht den Client-Code viel einfacher und leichter zu lesen.

SystemMessage withoutAttachment = new SystemMessage("title", "content");
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", attachment);

10
Das ist viel Kopieren + Einfügen, wenn nur ein oder zwei Absätze relevant sind.
Garret Wilson

47
Leider geht diese Erklärung nicht auf die Bedenken ein, wann der Anrufer z. B. das Parsen einiger Informationen optional ist. Bei der Methodenüberladung (wie oben empfohlen) muss der aufrufende Code lauten: "Ist X vorhanden? Wenn ja, rufe ich die überladene Methode A auf. Ist Y vorhanden? Ich muss die überladene Methode B aufrufen. Wenn X und Y vorhanden sind, muss ich überladene Methode C aufrufen. " Und so weiter. Es mag eine gute Antwort darauf geben, aber diese Erklärung des "Warum" deckt es nicht ab.
Garret Wilson

9
Auch bei Sammlungen gibt es einen deutlichen Unterschied zwischen einer leeren Sammlung und keiner Sammlung. Zum Beispiel ein Cache. War es ein Cache-Miss? leer optional / null. Gab es einen Cache-Treffer, der zufällig eine leere Sammlung war? voll optional / Sammlung.
Ajax

1
@ Ajax Ich denke, Sie verstehen den Artikel falsch. Sie zitieren einen Punkt, der im Effective Java- Buch befürwortet wird. Wenn ein Methodenrückgabetyp eine Sammlung ist (kein beliebiges Objekt, wie ein Cache zurückgeben würde), sollten Sie die Rückgabe einer leeren Sammlung anstelle von nulloder bevorzugen Optional.
Gili

4
Sicher, Sie sollten es bevorzugen , aber Sie haben nicht immer die Kontrolle über einen Code, und im wahrsten Sinne des Wortes möchten Sie vielleicht zwischen "Es gibt einen Wert und es ist eine leere Sammlung" und "Es gibt keinen" unterscheiden Wert definiert (noch) ". Da "magic null" auch eine entmutigte Praxis ist, liegt es an Ihnen, dem Entwickler, die am wenigsten schlechte Option zu wählen. Ich ziehe es leer optional eine Cache - Miss statt einer leeren Sammlung darzustellen, da der tatsächliche gecached Wert kann seine eine leere Auflistung.
Ajax

86

Es gibt fast keine guten Gründe, nicht Optionalals Parameter zu verwenden. Die Argumente dagegen beruhen auf Argumenten der Autorität (siehe Brian Goetz - sein Argument ist, dass wir keine Nicht-Null-Optionen erzwingen können) oder dass die OptionalArgumente null sein können (im Wesentlichen dasselbe Argument). Natürlich kann jede Referenz in Java null sein. Wir müssen dazu ermutigen, dass Regeln vom Compiler erzwungen werden, nicht vom Programmierspeicher (was problematisch ist und nicht skaliert).

Funktionale Programmiersprachen fördern OptionalParameter. Eine der besten Möglichkeiten, dies zu verwenden, besteht darin, mehrere optionale Parameter liftM2zu verwenden und eine Funktion zu verwenden, sofern die Parameter nicht leer sind, und eine optionale zurückzugeben (siehe http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/). data / Option.html # liftM2-fj.F- ). Java 8 hat leider eine sehr begrenzte Bibliothek implementiert, die optional unterstützt.

Als Java-Programmierer sollten wir nur null verwenden, um mit älteren Bibliotheken zu interagieren.


3
Wie wäre es stattdessen mit @Nonnullund @Nullablevon javax.annotation? Ich benutze sie ausgiebig in einer Bibliothek, die ich entwickle, und die Unterstützung durch die IDE (IntelliJ) ist sehr gut.
Rogério

1
Das ist in Ordnung, aber Sie müssen diese Annotation überall verwenden und benötigen eine Bibliothek, um Methoden wie Map, FlatMap / Bind, LiftM2, Sequenz usw. zu unterstützen.
Mark Perry

14
Funktionale Programmiersprachen fördern optionale Parameter. Zitieren erforderlich. Die meisten Funktionen sollten nicht optional sein. Stattdessen muss der Aufrufer der betreffenden Funktion (unter Verwendung geeigneter Funktionen höherer Ordnung) mit dem Vorhandensein oder Fehlen eines Werts umgehen.
Jub0bs

5
Diese. In der Tat ist keine der Antworten überzeugend genug, und keine von ihnen beantwortet diese spezielle Frage: "Warum wird die Verwendung von Optionen als Methodenparameter als schlechte Praxis angesehen, während das Annotieren von Methodenparametern mit @NonNullvöllig in Ordnung ist, wenn sie auf unterschiedliche Weise demselben Zweck dienen?" Für mich gibt es nur ein Argument, das zumindest einen Sinn ergibt: "Optional sollte verwendet werden, um bessere APIs mit einem eindeutigen Rückgabetyp bereitzustellen. Für Methodenargumente können jedoch überladene Funktionen verwendet werden." - Dennoch denke ich nicht, dass dies ein Argument ist, das stark genug ist.
Utku Özdemir

1
Natürlich lieber als null, aber was noch besser ist, ist, dass man nicht viele optionale Werte benötigt, weil gutes Design: D
yeoman

12

Dieser Rat ist eine Variante der Faustregel "Seien Sie in Bezug auf Eingaben so unspezifisch wie möglich und in Bezug auf Ausgaben so spezifisch wie möglich".

Wenn Sie eine Methode haben, die einen einfachen Wert ungleich Null annimmt, können Sie diesen normalerweise über den Wert zuordnen Optional, sodass die einfache Version in Bezug auf Eingaben streng unspezifischer ist. Es gibt jedoch eine Reihe möglicher Gründe, warum Sie Optionaldennoch ein Argument benötigen möchten :

  • Sie möchten, dass Ihre Funktion in Verbindung mit einer anderen API verwendet wird, die eine zurückgibt Optional
  • Ihre Funktion sollte etwas anderes als ein Leerzeichen zurückgeben, Optionalwenn der angegebene Wert leer ist
  • Sie finden Optionales so großartig, dass jeder, der Ihre API verwendet, dazu aufgefordert werden sollte, etwas darüber zu lernen ;-)

10

Das Muster mit Optionalist für einen, um eine Rückkehr zu vermeiden null. Es ist immer noch durchaus möglich, nullauf eine Methode überzugehen.

Obwohl diese noch nicht wirklich offiziell sind, können Sie Anmerkungen im JSR-308-Stil verwenden, um anzugeben, ob Sie nullWerte in die Funktion akzeptieren oder nicht . Beachten Sie, dass Sie über das richtige Tool verfügen müssen, um es tatsächlich zu identifizieren, und dass es eher eine statische Überprüfung als eine durchsetzbare Laufzeitrichtlinie bietet, aber es würde helfen.

public int calculateSomething(@NotNull final String p1, @NotNull final String p2) {}

Das Problem hier ist eher, dass @NotNull nicht der Standardfall ist und explizit kommentiert werden muss - daher Boilerplate und Dinge, die Leute aufgrund von Faulheit einfach nicht tun werden.
Dwegener

1
@dwegener Ja, aber Optionaldas Problem wird auch nicht behoben , daher mein Vorschlag für die Anmerkungen.
Makoto

2
Es ist viel schwieriger zu ignorieren Optional als eine Anmerkung zu ignorieren, die Sie nur bemerken, wenn Sie die Dokumente / Quellen lesen oder wenn Ihr Werkzeug sie abfangen kann (statische Analysen können die Nullbarkeit nicht immer korrekt bestimmen).
Ajax

Beachten Sie, dass @Nullable möglicherweise nicht bedeutet, dass Sie null als gültigen Wert "akzeptieren", dh keine Ausnahme auslösen. Dies kann nur bedeuten, dass Sie sich vor diesem Fall schützen, aber dennoch eine Ausnahme auslösen (dies ist die Findbugs-Semantik). Sie können also mit solchen Anmerkungen Mehrdeutigkeit anstelle von Klarheit einführen.
Tkruse

Ich bin nicht sicher, welche Mehrdeutigkeit es gibt @ user2986984. "Nicht null behandeln" kann nur realistisch bedeuten, dass eine Ausnahme ausgelöst wird.
Makoto

7

Das scheint mir ein bisschen albern, aber der einzige Grund, an den ich denken kann, ist, dass Objektargumente in Methodenparametern bereits in gewisser Weise optional sind - sie können null sein. Daher ist es sinnlos, jemanden zu zwingen, ein vorhandenes Objekt zu nehmen und es in ein optionales Objekt zu verpacken.

Abgesehen davon ist es eine vernünftige Sache, Methoden miteinander zu verketten, die Optionen annehmen / zurückgeben, z. B. vielleicht Monade.


7
Können Rückgabewerte und Felder nicht auch null sein? So Optionalist völlig sinnlos?
Samuel Edwin Ward


4

Meiner Meinung nach sollte Optional eine Monade sein, und diese sind in Java nicht denkbar.

In der funktionalen Programmierung beschäftigen Sie sich mit reinen Funktionen höherer Ordnung, die ihre Argumente nur basierend auf ihrem "Geschäftsdomänentyp" annehmen und zusammensetzen. Das Erstellen von Funktionen, die sich auf die reale Welt stützen oder deren Berechnung gemeldet werden sollte (sogenannte Nebenwirkungen), erfordert die Anwendung von Funktionen, die dafür sorgen, dass die Werte automatisch aus den Monaden entpackt werden, die die Außenwelt darstellen (Status, Konfiguration, Futures, vielleicht, entweder, Schriftsteller, etc ...); Dies nennt man Heben. Sie können sich das als eine Art Trennung von Bedenken vorstellen.

Das Mischen dieser beiden Abstraktionsebenen erleichtert die Lesbarkeit nicht, daher sollten Sie sie besser vermeiden.


3

Ein weiterer Grund, beim Übergeben eines Optionalas-Parameters vorsichtig zu sein, besteht darin, dass eine Methode eine Sache Optionalausführen sollte ... Wenn Sie einen Parameter übergeben, können Sie mehr als eine Sache bevorzugen, es könnte ähnlich sein, einen booleschen Parameter zu übergeben.

public void method(Optional<MyClass> param) {
     if(param.isPresent()) {
         //do something
     } else {
         //do some other
     }
 }

Ich denke, das ist kein guter Grund. Dieselbe Logik könnte angewendet werden, um zu überprüfen, ob param null ist, wenn nicht optional verwendet wird. Tatsächlich if(param.isPresent())ist dies nicht der beste Ansatz, um Optional zu verwenden. Stattdessen können Sie param.forEach(() -> {...})
Folgendes

Ich mag die Idee, nullfähige Parameter zu übergeben, auch nicht. Wie auch immer, ich sagte, Sie könnten es bevorzugen, natürlich gibt es Ausnahmen, die Sie verwenden können. Es liegt an Ihnen, verwenden Sie es nur vorsichtig, das ist alles. Es gibt keine Regel, die für alle Szenarien gilt.
Pau

2

Das Akzeptieren von Optional als Parameter führt zu unnötigem Umbruch auf Anruferebene.

Zum Beispiel im Fall von:

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {}

Angenommen, Sie haben zwei Nicht-Null-Zeichenfolgen (dh von einer anderen Methode zurückgegeben):

String p1 = "p1"; 
String p2 = "p2";

Sie müssen sie in Optional einwickeln, auch wenn Sie wissen, dass sie nicht leer sind.

Dies wird noch schlimmer, wenn Sie mit anderen "abbildbaren" Strukturen komponieren müssen, d. H. Eithers :

Either<Error, String> value = compute().right().map((s) -> calculateSomething(
< here you have to wrap the parameter in a Optional even if you know it's a 
  string >));

ref:

Methoden sollten Option nicht als Parameter erwarten. Dies ist fast immer ein Codegeruch, der auf einen Verlust des Kontrollflusses vom Anrufer zum Angerufenen hinweist. Es sollte in der Verantwortung des Anrufers liegen, den Inhalt einer Option zu überprüfen

ref. https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749


1

Ich denke, das liegt daran, dass Sie normalerweise Ihre Funktionen schreiben, um Daten zu manipulieren, und sie dann auf die OptionalVerwendung mapund ähnlicher Funktionen heben . Dies fügt das Standardverhalten hinzu Optional. Natürlich kann es Fälle geben, in denen es notwendig ist, eine eigene Hilfsfunktion zu schreiben, die funktioniert Optional.


1

Ich glaube, die Resonanz des Seins besteht darin, dass Sie zuerst prüfen müssen, ob Optional selbst null ist oder nicht, und dann versuchen müssen, den Wert zu bewerten, den es umschließt. Zu viele unnötige Validierungen.


2
Übergeben einer Null Optionale Referenz kann als Programmierfehler angesehen werden (da Optional eine explizite Möglichkeit zum Übergeben eines "leeren" Werts bietet ). Es lohnt sich daher nicht, dies zu überprüfen, es sei denn, Sie möchten in allen Fällen vermeiden, Ausnahmen auszulösen.
Pvgoran

1

Optionals sind nicht für diesen Zweck konzipiert, wie Brian Goetz ausführlich erklärt hat .

Sie können jederzeit @Nullable verwenden , um anzugeben , dass ein Methodenargument null sein kann. Wenn Sie eine Option verwenden, können Sie Ihre Methodenlogik nicht sauberer schreiben.


5
Es tut mir leid, aber ich kann dem Argument "wurde nicht entworfen" oder "jemand empfiehlt dagegen" nicht zustimmen. Als Ingenieur sollten wir spezifisch sein. Die Verwendung von @Nullable ist viel schlimmer als die Verwendung von Optional, da Optionals aus API-Sicht viel ausführlicher sind. Ich sehe keine guten Gründe gegen die Verwendung von Optionals als Argumente (vorausgesetzt, Sie möchten keine Methodenüberladung verwenden)
Kacper86

0

Ein weiterer Ansatz, den Sie tun können, ist

// get your optionals first
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

// bind values to a function
Supplier<Integer> calculatedValueSupplier = () -> { // your logic here using both optional as state}

Sobald Sie eine Funktion erstellt haben (in diesem Fall Lieferant), können Sie diese wie jede andere Variable weitergeben und mit aufrufen

calculatedValueSupplier.apply();

Die Idee hier ist, ob Sie einen optionalen Wert haben oder nicht, ein internes Detail Ihrer Funktion und kein Parameter. Denkfunktionen beim Nachdenken über optional als Parameter sind tatsächlich sehr nützliche Techniken, die ich gefunden habe.

Die Frage, ob Sie es tatsächlich tun sollten oder nicht, hängt von Ihrer Präferenz ab, aber wie andere sagten, macht es Ihre API gelinde gesagt hässlich.


0

Anfangs habe ich es auch vorgezogen, Optionals als Parameter zu übergeben, aber wenn Sie von einer API-Designer-Perspektive zu einer API-User-Perspektive wechseln, sehen Sie die Nachteile.

In Ihrem Beispiel, in dem jeder Parameter optional ist, würde ich vorschlagen, die Berechnungsmethode wie folgt in eine eigene Klasse zu ändern:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

MyCalculator mc = new MyCalculator();
p1.map(mc::setP1);
p2.map(mc::setP2);
int result = mc.calculate();

0

Ich weiß, dass es bei dieser Frage eher um Meinungen als um harte Fakten geht. Aber ich bin kürzlich von einem .net-Entwickler zu einem Java-Entwickler gewechselt, deshalb bin ich erst kürzlich der optionalen Partei beigetreten. Ich würde es auch vorziehen, dies als Kommentar anzugeben, aber da meine Punktebene es mir nicht erlaubt, Kommentare abzugeben, bin ich gezwungen, dies stattdessen als Antwort zu verwenden.

Was ich getan habe, hat mir als Faustregel gute Dienste geleistet. Ist es, Optionals für Rückgabetypen zu verwenden und Optionals nur als Parameter zu verwenden, wenn ich sowohl den Wert des Optionals als auch das Wetter benötige oder nicht, hatte das Optionale einen Wert innerhalb der Methode.

Wenn ich mich nur um den Wert kümmere, überprüfe ich vor dem Aufrufen der Methode isPresent. Wenn die Methode eine Protokollierung oder eine andere Logik enthält, die davon abhängt, ob der Wert vorhanden ist, übergebe ich gerne die Option.


0

Dies liegt daran, dass wir unterschiedliche Anforderungen an einen API-Benutzer und einen API-Entwickler haben.

Ein Entwickler ist dafür verantwortlich, eine genaue Spezifikation und eine korrekte Implementierung bereitzustellen. Wenn dem Entwickler bereits bekannt ist, dass ein Argument optional ist, muss die Implementierung korrekt damit umgehen, unabhängig davon, ob es sich um eine Null oder eine Option handelt. Die API sollte für den Benutzer so einfach wie möglich sein, und null ist die einfachste.

Andererseits wird das Ergebnis vom API-Entwickler an den Benutzer übergeben. Obwohl die Spezifikation vollständig und ausführlich ist, besteht immer noch die Möglichkeit, dass der Benutzer sie entweder nicht bemerkt oder nur faul damit umgeht. In diesem Fall zwingt das optionale Ergebnis den Benutzer, zusätzlichen Code zu schreiben, um mit einem möglichen leeren Ergebnis umzugehen.


0

Wenn Sie Methode 3 verwenden, können Sie zunächst die letzten 14 Codezeilen durch folgende ersetzen:

int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));

Die vier Variationen, die Sie geschrieben haben, sind Convenience- Methoden. Sie sollten sie nur verwenden, wenn sie bequemer sind. Das ist auch der beste Ansatz. Auf diese Weise ist der API sehr klar, welche Mitglieder erforderlich sind und welche nicht. Wenn Sie nicht vier Methoden schreiben möchten, können Sie die Dinge durch die Benennung Ihrer Parameter klären:

public int calculateSomething(String p1OrNull, BigDecimal p2OrNull)

Auf diese Weise ist klar, dass Nullwerte zulässig sind.

Ihre Verwendung von p1.orElse(null)zeigt, wie ausführlich unser Code bei Verwendung von Optional wird, weshalb ich ihn vermeide. Optional wurde für die funktionale Programmierung geschrieben. Streams brauchen es. Ihre Methoden sollten wahrscheinlich niemals Optional zurückgeben, es sei denn, Sie müssen sie für die funktionale Programmierung verwenden. Es gibt Methoden wie Optional.flatMap()Methoden, die einen Verweis auf eine Funktion erfordern, die Optional zurückgibt. Hier ist seine Unterschrift:

public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)

Dies ist normalerweise der einzige gute Grund, eine Methode zu schreiben, die Optional zurückgibt. Aber auch dort kann es vermieden werden. Sie können einen Getter, der Optional nicht zurückgibt, an eine Methode wie flatMap () übergeben, indem Sie ihn in eine andere Methode einschließen, die die Funktion in den richtigen Typ konvertiert. Die Wrapper-Methode sieht folgendermaßen aus:

public static <T, U> Function<? super T, Optional<U>> optFun(Function<T, U> function) {
    return t -> Optional.ofNullable(function.apply(t));
}

Angenommen, Sie haben einen Getter wie diesen: String getName()

Sie können es nicht wie folgt an flatMap weitergeben:

opt.flatMap(Widget::getName) // Won't work!

Aber Sie können es so weitergeben:

opt.flatMap(optFun(Widget::getName)) // Works great!

Außerhalb der funktionalen Programmierung sollten Optionals vermieden werden.

Brian Goetz sagte es am besten, als er dies sagte:

Der Grund, warum Optional zu Java hinzugefügt wurde, ist folgender:

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .findFirst()
    .getOrThrow(() -> new InternalError(...));

ist sauberer als das:

Method matching =
    Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .getFirst();
if (matching == null)
  throw new InternalError("Enclosing method not found");
return matching;

1
Nun, das ist einer der Gründe, warum es zu Java hinzugefügt wurde. Es gibt viele andere. Das Ersetzen langer Ketten verschachtelter 'if'-Anweisungen, die in eine Datenstruktur übergehen, durch eine einzelne Folge verketteter Aufrufe von Optional.map ist mein persönlicher Favorit. Die Programmierwelt für funktionale Sprachen bietet neben dem einfachen Ersetzen solcher Nullprüfungen viele interessante Verwendungsmöglichkeiten für Optional.
Ein
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.