2014-05-01 10 views
12

Java 8 ha un numero di classi facoltative, come OptionalDouble, OptionalInt, OptionalLong.Operazioni su Java 8 Valori facoltativi *.

C'è un modo piacevole di operare con valori Opzionali dello stesso tipo? Cioè, mi piacerebbe essere in grado di fare:

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

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

E, naturalmente, se qualcuno di loro sono "vuoto" il risultato dovrebbe essere vuoto. Dopo aver cercato su google un po 'ho trovato alcuni esempi di codice in cui le persone utilizzano tali funzioni (ad esempio add) ma non fa parte dell'API (più?).

risposta

3

Strano. Il riferimento Optional ha un metodo map che è possibile utilizzare per fare qualcosa di simile a quello che si desidera, ma sembra essere assente dalle opzioni primitive. Credo che il tuo unico ricorso al momento sia quello di utilizzare OptionalDouble::isPresent o OptionalDouble::ifPresent.

Oppure è possibile definire i propri metodi di supporto add o definire la propria classe OptionalDouble per includere tali metodi.

+2

'Opzionale # Map' sembra aver bisogno di un' Function', non un 'BinaryOperator', quindi funzionerebbe solo con funzioni che operano su un valore, non cose come' add' o 'multiply'. – ajb

+0

Ho usato un metodo di supporto ma non capisco il motivo per cui hanno omesso questi metodi, o almeno la possibilità di aggiungerli.Si noti che le classi 'Optional' per primitive come' OptionalDouble' sono 'final' quindi non possono essere estese. – siki

+1

@siki Sono finali perché gli optionals sono classi immutabili e quindi non dovrebbero mai essere estesi, poiché non è più possibile garantire l'immutabilità se un metodo accetta come parametro "OptionalDouble". – skiwi

3

Si potrebbe fare una propria implementazione che racchiude in sé un OptionalDouble, una cosa molto importante da considerare è che la classe incapsulare dovrebbe essere immutabili per evitare confusione come OptionalDouble è di per sé immutabile.

Un'implementazione personalizzata è preferibile rispetto al metodo statico per la leggibilità.

Sono andato avanti e creato il mio, con il comportamento più semplice possibile:

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 + "]"; 
    } 
} 

che può quindi essere utilizzato come:

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

userei pianura 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 

Non solo è più veloce e più breve, è anche più semplice.

3

Lo scopo principale di Optional è rappresentare la funzione return value that might be absent.

Il punto di avere specializzazioni primitive di flussi è di evitare il sovraccarico di boxing/unboxing. Con OptionalInt e gli amici c'è un livello inevitabile di boxe (che sarebbe peggio se non esistessero, in quanto l'alternativa sarebbe Optional<Integer>), ma l'intento è per chiunque elabori il valore di ritorno per annullarlo immediatamente (o fornire un valore predefinito o lanciare un'eccezione se è assente) e poi occuparsi di primitivi effettivi da quel momento in poi.

Il supporto di tutte le API aggiuntive per eseguire operazioni aritmetiche, confronti ecc. Su primitive facoltative aggiungerebbe ancora più rigonfiamenti API. Usarlo porterebbe a un codice lento e disordinato, piuttosto uno svantaggio rispetto alla sintassi dell'espressione aritmetica perfettamente precisa che esiste già in Java. In breve, l'aggiunta di un gruppo di operazioni su primitive facoltative non è stata considerata utile.