Was ist der Sinn von Guavas optionaler Klasse?

89

Ich habe kürzlich darüber gelesen und Leute gesehen, die diese Klasse verwenden, aber in fast allen Fällen nullhätte die Verwendung auch funktioniert - wenn nicht intuitiver. Kann jemand ein konkretes Beispiel geben, wo Optionaletwas erreicht werden nullkönnte, das nicht oder viel sauberer wäre? Das einzige, woran ich denken kann, ist, es damit zu verwenden, Mapsdass keine nullSchlüssel akzeptiert werden , aber selbst das könnte mit einer seitlichen "Zuordnung" des Nullwerts geschehen. Kann mir jemand ein überzeugenderes Argument liefern? Danke dir.

STRAHL
quelle
12
zufälliges Geschwätz: Ich hasse es, wenn Leute ein "Muster" überbeanspruchen und den Code für einen theoretischen Vorteil, der nicht existiert, so hässlich aussehen lassen ...
RAY
Ab Java8 würde ich diese Guava-Klasse nicht mehr verwenden, da sie weniger leistungsfähig ist als die JDK-Klasse. Siehe stackoverflow.com/a/10756992/82609
Sebastien Lorber

Antworten:

155

Guave Teammitglied hier.

Der wahrscheinlich größte Nachteil nullist, dass es nicht offensichtlich ist, was es in einem bestimmten Kontext bedeuten sollte: Es hat keinen illustrativen Namen. Es ist nicht immer offensichtlich, dass nulldies "kein Wert für diesen Parameter" bedeutet - zum Teufel, als Rückgabewert bedeutet es manchmal "Fehler" oder sogar "Erfolg" (!!) oder einfach "die richtige Antwort ist nichts". Optionalist häufig das Konzept, das Sie tatsächlich meinen, wenn Sie eine Variable auf Null setzen, aber nicht immer. Wenn dies nicht der Fall ist, empfehlen wir Ihnen, eine eigene Klasse zu schreiben, ähnlich wie, Optionaljedoch mit einem anderen Namensschema, um zu verdeutlichen, was Sie tatsächlich meinen.

Aber ich würde sagen, der größte Vorteil Optionalliegt nicht in der Lesbarkeit: Der Vorteil ist die Idiotensicherheit. Es zwingt Sie, aktiv über den fehlenden Fall nachzudenken, wenn Sie möchten, dass Ihr Programm überhaupt kompiliert wird, da Sie den OptionalFall aktiv auspacken und ansprechen müssen. Null macht es beunruhigend einfach, Dinge einfach zu vergessen, und obwohl FindBugs hilft, denke ich, dass es das Problem nicht annähernd so gut angeht. Dies ist besonders relevant, wenn Sie Werte zurückgeben, die möglicherweise "vorhanden" sind oder nicht. Es ist weitaus wahrscheinlicher, dass Sie (und andere) vergessen, dass other.method(a, b)ein nullWert azurückgegeben werden könnte, nullals dass Sie dies bei der Implementierung vergessen könnten other.method. RückkehrOptional macht es Anrufern unmöglich, diesen Fall zu vergessen, da sie das Objekt selbst auspacken müssen.

Aus diesen Gründen empfehlen wir, dass Sie Optionalals Rückgabetyp für Ihre Methoden verwenden, jedoch nicht unbedingt in Ihren Methodenargumenten.

(Dies ist übrigens völlig aus der Diskussion hier gestrichen .)

Louis Wasserman
quelle
3
+1 für eine gute Erklärung. Ich weiß nicht, ob dies die Inspiration ist, aber ich würde einen Zeiger auf Optionstypen in SML, OCaml und F # hinzufügen, die viele der gleichen Semantik haben.
Adam Mihalcin
2
Es ist immer toll, eine Antwort von jemandem zu bekommen, der direkt involviert war. Ich hätte + 1 nur dafür. Würde +1 mehr für eine gute Antwort. (aber ich kann nicht.) Ich denke, der Punkt, dass es ein Rückgabewert ist, Verbraucher einer unbekannten Methode zu zwingen, sich die Mühe zu machen, anzuerkennen, dass ein "Nichts" zurückgegeben werden kann, ist ein zwingender Grund. Ich mag es nicht, wie es überbeansprucht (oder sogar missbraucht) wird. Ich fühle mich wirklich unwohl, wenn ich sehe, wie Leute Optionals als Werte in Maps einfügen. Map Returing Null für einen nicht existierenden / nicht zugeordneten Schlüssel ist ein etabliertes Paradigma ... Keine Notwendigkeit, es komplizierter zu machen ...
RAY
9
Es ist gut etabliert , dass MapErträge , nullwenn ein Schlüssel ist , nicht zugeordnete, aber erinnern , dass , wenn Sie das tun map.put(key, null), dann map.containsKey(key)zurückkehren wird , trueaber map.get(key)wird zurückkehren null. Optionalkann nützlich sein, um die Unterscheidung zwischen dem Fall "explizit auf Null abgebildet" und dem Fall "Nicht in der Karte vorhanden" zu klären. Ich gebe zu, dass Optionaldies missbraucht werden kann, aber ich bin noch nicht davon überzeugt, dass der von Ihnen beschriebene Fall ein Missbrauch ist.
Louis Wasserman
8
Um eine antiquierte Analogie zu verwenden, gab es diese riesigen Dinge, die "Telefonbücher" genannt wurden :-) und wenn ich Sie bat, die Nummer von jemandem nachzuschlagen, und Sie sagten "es gibt keine Nummer für diese Person", würde ich sagen "was machen Sie? meine, sie sind mit einer nicht aufgelisteten Nummer da drin oder du hast einfach überhaupt keinen Eintrag für sie gefunden? " Diese beiden möglichen Antworten lassen sich gut auf Optional.absent () bzw. null abbilden. Optional.absent () ist das definitive "positive negative".
Kevin Bourrillion
3
@RAY: In gewisser Weise Optional<T> ist das der Wert "gefunden, aber nicht gültig". Oder genauer gesagt, Optional<T>ist eine Art und Weise zu schmücken jede Art Tmit einem zusätzlichen „gefunden, aber nicht gültig“ Wert - eine neue Art zu schaffen , durch die Kombination von zwei vorhandenen Typen. Wenn Sie hundert Klassen haben, kann es schwierig werden, für jede Klasse einen eigenen "gefundenen, aber nicht gültigen" Wert zu erstellen, der jedoch Optional<T>für alle problemlos funktioniert.
Daniel Pryden
9

Es sieht wirklich aus wie das MaybeMonadenmuster von Haskell.

Sie sollten Folgendes lesen, Wikipedia Monad (funktionale Programmierung) :

Und lesen Von Optional zu Monad mit Guava auf Blog Kerflyn ist, die diskutiert über die Fakultativ von Guava als Monade verwendet:


Bearbeiten: Mit Java8 gibt es eine integrierte Option, die monadische Operatoren wie hat flatMap. Dies war ein kontroverses Thema, wurde aber schließlich umgesetzt.

Siehe http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html

public Optional<String> tryFindSimilar(String s)  //...

Optional<Optional<String>> bad = opt.map(this::tryFindSimilar);
Optional<String> similar =       opt.flatMap(this::tryFindSimilar);

Der flatMapBediener ist wichtig, um monadische Operationen zuzulassen, und ermöglicht das einfache Verketten von Anrufen, die alle optionale Ergebnisse zurückgeben.

Denken Sie darüber nach, wenn Sie den mapOperator 5 Mal verwendet haben, erhalten Sie einen Optional<Optional<Optional<Optional<Optional<String>>>>>, während flatMapSie ihn verwendenOptional<String>

Seit Java8 würde ich Guavas Optional, das weniger leistungsfähig ist, lieber nicht verwenden.

Sebastien Lorber
quelle
6

Ein guter Grund, es zu verwenden, ist, dass es Ihre Nullen sehr aussagekräftig macht. Anstatt eine Null zurückzugeben, die viele Dinge bedeuten könnte (wie Fehler oder Misserfolg oder leer usw.), können Sie Ihrer Null einen 'Namen' geben. Schauen Sie sich dieses Beispiel an:

Definieren wir ein grundlegendes POJO:

class PersonDetails {

String person;
String comments;

public PersonDetails(String person, String comments) {
    this.person = person;
    this.comments = comments;
}

public String getPerson() {
    return person;
}


public String getComments() {
    return comments;
}

}}

Nutzen wir nun dieses einfache POJO:

public Optional<PersonDetails> getPersonDetailstWithOptional () {

  PersonDetails details = null; /*details of the person are empty but to the caller this is meaningless,
  lets make the return value more meaningful*/


    if (details == null) {
      //return an absent here, caller can check for absent to signify details are not present
        return Optional.absent();
    } else {
      //else return the details wrapped in a guava 'optional'
        return Optional.of(details);   
    }
}

Vermeiden wir jetzt die Verwendung von null und führen Sie unsere Überprüfungen mit Optional durch, damit dies sinnvoll ist

public void checkUsingOptional () {

    Optional<PersonDetails> details = getPersonDetailstWithOptional();

    /*below condition checks if persons details are present (notice we dont check if person details are null,
    we use something more meaningful. Guava optional forces this with the implementation)*/
    if (details.isPresent()) {

      PersonDetails details = details.get();

        // proceed with further processing
        logger.info(details);

    } else {
        // do nothing
        logger.info("object was null"); 
    }

    assertFalse(details.isPresent());
}

Somit ist es am Ende eine Möglichkeit, Nullen sinnvoll und weniger mehrdeutig zu machen.

j2emanue
quelle
4

Der wichtigste Vorteil von Optional besteht darin, dass dem Vertrag zwischen dem Implementierer und dem Aufrufer einer Funktion weitere Details hinzugefügt werden. Aus diesem Grund ist sowohl für Parameter als auch für den Rückgabetyp nützlich.

Wenn Sie die Konvention so gestalten, dass sie immer Optionalmögliche Nullobjekte enthält, fügen Sie Fälle wie:

  1. Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)

    Der Vertrag hier legt klar fest, dass die Möglichkeit besteht, dass ein Ergebnis nicht zurückgegeben wird, zeigt aber auch, dass es mit fromund toals abwesend funktioniert .

  2. Optional<Integer> maxPrime(Optional<Integer> from, Integer to)

    Der Vertrag legt fest, dass das von optional ist, sodass ein fehlender Wert eine besondere Bedeutung haben kann, z. B. ab 2. Ich kann davon ausgehen, dass ein Nullwert für den toParameter eine Ausnahme auslöst.

Der gute Teil der Verwendung von Optional besteht darin, dass der Vertrag sowohl beschreibend (ähnlich wie bei @NotNullAnmerkungen) als auch formal wurde, da Sie Code schreiben müssen, um .get()damit fertig zu werden Optional.

Rosercostin
quelle
Warum eine optionale <Liste> verwenden, wenn eine leere Liste sauberer wäre?
RAY
Bei der Rücksendung habe ich das falsche Beispiel gewählt. Mit Sammlungen können Sie die Konvention festlegen, dass immer eine leere Sammlung anstelle eines Nullwerts zurückgegeben wird. Wenn diese Konvention verwendet wird, benötigen Sie keine optionalen Sammlungen. Optional kann eine Sammlung mit null oder einem Element wahrgenommen werden, sodass es nicht erforderlich ist, sie um eine andere Sammlung zu legen.
Raisercostin
2
Je nach Kontext kann es einen bedeutenden Unterschied zwischen einer Liste mit 0 Elementen und einer fehlenden Liste geben.
Plasma147