2016-01-11 11 views
7

Ho un Optional che voglio "convertire" in un OptionalInt, ma non sembra essere un modo semplice per farlo.Ottenere un OptionalInt da Opzionale

Qui è quello che voglio fare (esempio inventato):

public OptionalInt getInt() { 
    return Optional.ofNullable(someString).filter(s -> s.matches("\\d+")).mapToInt(Integer::parseInt); 
} 

ma non c'è nessun metodo per mapToInt()Optional.

Il meglio che ho potuto venire in mente è:

return Optional.ofNullable(someString) 
    .filter(s -> s.matches("\\d+")) 
    .map(s -> OptionalInt.of(Integer.parseInt(s))) 
    .orElse(OptionalInt.empty()); 

ma che sembra poco elegante.

Mi manca qualcosa del JDK che può rendere la conversione più elegante?

risposta

5

Mentre il codice non è più leggibile di un'espressione condizionale normale, c'è una soluzione semplice:

public OptionalInt getInt() { 
    return Stream.of(someString).filter(s -> s != null && s.matches("\\d+")) 
     .mapToInt(Integer::parseInt).findAny(); 
} 

con Java 9, è possibile utilizzare

public OptionalInt getInt() { 
    return Stream.ofNullable(someString).filter(s -> s.matches("\\d+")) 
     .mapToInt(Integer::parseInt).findAny(); 
} 

Come detto , non è più leggibile di una normale espressione condizionale, ma penso che sia ancora meglio dell'uso di mapOrElseGet (e la prima variante non ha bisogno di Java 9.

+0

Questa è una buona idea. Inoltre, il mio cattivo re 'of' vs' ofNullable() ':. Ho semplificato l'esempio. Dovrebbe essere stato 'Optional.ofNullable()'. Ho aggiornato la Q e potato la tua risposta di conseguenza. – Bohemian

2

No, non c'è modo di farlo in modo più elegante utilizzando l'API Java standard. E per quanto ne so non è previsto l'aggiunta di tali metodi in JDK-9. Ho chiesto a Paul Sandoz sull'aggiunta mapToInt, ecc, here s' la sua risposta:

Me:

non è una buona idea per fornire anche un modo per trasferire tra Optional tipi come mapToInt, mapToObj, ecc., come è stato fatto in Stream API?

Paul:

io non voglio andare lì, la mia risposta è trasformare Optional* in un *Stream. Un argomento per l'aggiunta di mapOrElseGet (si noti che le varianti primitive restituiscono U) è che un'altra funzionalità può essere composta da esso.

Così si rischia di avere in Java-9:

return Optional.of(someString).filter(s -> s.matches("\\d+")) 
    .mapOrElseGet(s -> OptionalInt.of(Integer.parseInt(s)), OptionalInt::empty); 

ma niente di più.

Questo perché gli autori di JDK insistono sul fatto che la classe Optional e i suoi amici primitivi (soprattutto gli amici primitivi) non dovrebbero essere ampiamente utilizzati, è solo un modo conveniente per eseguire un insieme limitato di operazioni sul valore di ritorno dei metodi che possono restituire " l'assenza del valore ". Anche gli optionals primitivi sono progettati per il miglioramento delle prestazioni, ma in realtà è molto meno significativo rispetto agli stream, quindi anche l'uso di Optional<Integer> va bene. Con il progetto Valhalla (si spera di arrivare in Java-10) si sarà in grado di utilizzare Optional<int> e OptionalInt diventerà inutile.

Nel tuo caso particolare, il modo migliore per farlo è usare operatore ternario:

return someString != null && someString.matches("\\d+") ? 
     OptionalInt.of(Integer.parseInt(someString)) : OptionalInt.empty(); 

Presumo che si desidera restituire il OptionalInt dal metodo. Altrimenti è ancora più discutibile perché ne avresti bisogno.

+0

ho fatto l'esempio più semplice possibile, ma avrei dovuto dire che non è così semplice come una == null ' 'test - Ho anche un' filter() ', e sì, lo sto restituendo da un metodo. Aggiornerò il Q – Bohemian

+1

@Bohemian, aggiornando la risposta. Ancora non c'è nulla nella classe 'Optional' che non può essere fatta usando i costrutti Java 1.0. –

+0

Anche in questo caso, l'esempio del filtro è forzato: ho cercato di mantenerlo SSCCE. Il mio caso d'uso non è così semplice che un ternario funzionerebbe bene. Ti darò il sigaro proverbiale però :) – Bohemian

1

Se avete qualsiasi oggetto e non solo un String, si può temporaneamente passare attraverso un Stream:

public static <T> OptionalInt toOptionalInt(Optional<T> optional, ToIntFunction<? super T> func) { 
    return optional.map(Stream::of).orElseGet(Stream::empty) 
    .mapToInt(func) 
    .findFirst(); 
} 

Questa soluzione ha il vantaggio di essere un one-liner, significa che è possibile copiare/incollare il contenuto di il metodo e basta cambiare func per quello che vuoi. Lo svantaggio sta attraversando un flusso per ottenere ciò che desideri. Ma se vuoi un generico one-liner, è questo.

Se si desidera un metodo di utilità, probabilmente preferisce utilizzare il seguente:

public static <T> OptionalInt toOptionalInt(Optional<T> optional, ToIntFunction<? super T> func) { 
    if (optional.isPresent()) { 
    return OptionalInt.of(func.applyAsInt(optional.get())); 
    } else { 
    return OptionalInt.empty(); 
    } 
} 
+0

Questa risposta esiste perché altre risposte non provengono da un 'Optional' come chiede il titolo, ma piuttosto ne costruiscono una, e prendono alcune libertà con la domanda del titolo che non prendo. –