2014-05-01 12 views
12

Java 8 verfügt über eine Reihe optionaler Klassen, z. B. OptionalDouble, OptionalInt, OptionalLong.Operationen auf Java 8 Optionale * Werte.

Gibt es eine gute Möglichkeit, mit optionalen Werten der gleichen Art zu arbeiten? Das heißt, würde Ich mag Lage sein, zu tun: Kurs

OptionalDouble d1 = OptionalDouble.of(1.); 
OptionalDouble d2 = OptionalDouble.of(2.); 
OptionalDouble d3 = OptionalDouble.of(3.); 

OptionalDouble result = d1.add(d2).multiply(d3); 

Und wenn einer von ihnen „leer“ ist das Ergebnis sollte leer sein. Nachdem ich etwas gegoogelt habe, habe ich ein paar Codebeispiele gefunden, in denen Leute diese Funktionen verwenden (z. B. add), aber es ist nicht der Teil der API (mehr?).

Antwort

3

Seltsam. Die Referenz Optional hat eine map-Methode, die Sie verwenden könnten, etwas zu tun, was Sie wollen, aber es scheint in den primitiven Optionals abwesend zu sein. Ich glaube, Ihre einzige Möglichkeit besteht derzeit darin, OptionalDouble::isPresent oder OptionalDouble::ifPresent zu verwenden.

Oder Sie könnten Ihre eigenen Helfer add Methoden definieren oder Ihre eigene OptionalDouble Klasse definieren, um diese Methoden einzuschließen.

+2

'Optionale # Map' scheint eine' Funktion' zu benötigen, nicht einen 'BinaryOperator', also würde sie nur mit Funktionen funktionieren, die auf einen Wert wirken, nicht auf Dinge wie' add' oder 'multiply'. – ajb

+0

Ich habe eine Hilfsmethode verwendet, aber ich verstehe nicht wirklich den Grund, warum sie diese Methoden weggelassen haben, oder zumindest die Fähigkeit, sie hinzuzufügen.Beachten Sie, dass die 'Optional' Klassen für Primitive wie' OptionalDouble' 'final' sind, so dass sie nicht einmal erweitert werden können. – siki

+1

@siki Sie sind endgültig, da Optionals unveränderliche Klassen sind und daher niemals erweitert werden sollten, da Sie keine Unveränderlichkeit mehr garantieren können, wenn eine Methode einen 'OptionalDouble' als Parameter akzeptiert. – skiwi

3

Sie könnten Ihre eigene Implementierung, die eine OptionalDouble kapselt, eine sehr wichtige Sache hier zu berücksichtigen ist, dass Ihre Kapselung Klasse sollte unveränderlich sein, um Verwirrung zu vermeiden, da OptionalDouble selbst unveränderlich ist.

Eine eigene Implementierung wird der statischen Lesbarkeit vorgezogen.

Ich ging weiter und meine eigenen erstellt, mit dem grundlegendsten Verhalten wie möglich:

public class OptionalDoubleImproved { 
    private static final OptionalDoubleImproved EMPTY = OptionalDoubleImproved.of(OptionalDouble.empty()); 

    private final OptionalDouble optionalDouble; 

    private OptionalDoubleImproved(final OptionalDouble optionalDouble) { 
     this.optionalDouble = Objects.requireNonNull(optionalDouble); 
    } 

    public static OptionalDoubleImproved of(final OptionalDouble optionalDouble) { 
     return new OptionalDoubleImproved(optionalDouble); 
    } 

    public OptionalDoubleImproved applyFunction(final DoubleBinaryOperator operator, final OptionalDouble operand) { 
     Objects.requireNonNull(operator); 
     Objects.requireNonNull(operand); 
     if (!optionalDouble.isPresent() || !operand.isPresent()) { 
      return EMPTY; 
     } 
     return OptionalDoubleImproved.of(OptionalDouble.of(operator.applyAsDouble(optionalDouble.getAsDouble(), operand.getAsDouble()))); 
    } 

    public OptionalDouble get() { 
     return optionalDouble; 
    } 

    @Override 
    public int hashCode() { 
     int hash = 7; 
     hash = 53 * hash + Objects.hashCode(this.optionalDouble); 
     return hash; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (obj == null) { 
      return false; 
     } 
     if (getClass() != obj.getClass()) { 
      return false; 
     } 
     final OptionalDoubleImproved other = (OptionalDoubleImproved) obj; 
     if (!Objects.equals(this.optionalDouble, other.optionalDouble)) { 
      return false; 
     } 
     return true; 
    } 

    @Override 
    public String toString() { 
     return "OptionalDoubleImproved[" + optionalDouble + "]"; 
    } 
} 

, die dann als verwendet werden können:

OptionalDouble d1 = OptionalDouble.of(1.); 
OptionalDouble d2 = OptionalDouble.of(2.); 
OptionalDouble d3 = OptionalDouble.of(3.); 

OptionalDouble result = OptionalDoubleImproved.of(d1) 
     .applyFunction((a, b) -> a + b, d2) 
     .applyFunction((a, b) -> a * b, d3) 
     .get(); 
2

Ich würde verwenden Ebene double

double d1 = 1, d2 = 2, d3 = 3; 
if (condition) 
    d1 = Double.NaN; 
double result = (d1 + d2) * d3; // if any double is NaN, the result is NaN 

Nicht nur ist das schneller und kürzer, es ist auch einfacher.

3

Der Hauptzweck von Optional ist die Darstellung einer Funktion return value that might be absent.

Der Punkt, primitive Spezialisierungen von Streams zu haben, ist zu vermeiden, Boxen/Unboxing Overhead. Mit OptionalInt und Freunden gibt es eine unvermeidliche Ebene des Boxens (die schlimmer wäre, wenn sie nicht existierten, als Alternative wäre Optional<Integer>), aber die Absicht ist für wer auch immer den Rückgabewert verarbeitet, um es sofort zu entpacken (oder einen Standard oder wirf eine Ausnahme aus, wenn sie abwesend ist) und behandle von da an die eigentlichen Primitiven.

Die Unterstützung aller zusätzlichen APIs für Arithmetik, Vergleiche usw. auf optionalen Primitiven würde sogar noch mehr API-Bloat hinzufügen. Die Verwendung würde zu einem überladenen, langsamen Code führen, ein ziemlicher Nachteil gegenüber der vollkommen feinen arithmetischen Ausdruckssyntax, die in Java bereits existiert. Kurz gesagt, das Hinzufügen einer Menge von Operationen an optionalen Primitiven wurde nicht als nützlich erachtet.