Qual è la differenza tra questi due metodi: Optional.flatMap()
e Optional.map()
?
Un esempio sarebbe apprezzato.
Stream#flatMap
e Optional#flatMap
.
Qual è la differenza tra questi due metodi: Optional.flatMap()
e Optional.map()
?
Un esempio sarebbe apprezzato.
Stream#flatMap
e Optional#flatMap
.
Risposte:
Utilizzare map
se la funzione restituisce l'oggetto necessario o flatMap
se la funzione restituisce un Optional
. Per esempio:
public static void main(String[] args) {
Optional<String> s = Optional.of("input");
System.out.println(s.map(Test::getOutput));
System.out.println(s.flatMap(Test::getOutputOpt));
}
static String getOutput(String input) {
return input == null ? null : "output for " + input;
}
static Optional<String> getOutputOpt(String input) {
return input == null ? Optional.empty() : Optional.of("output for " + input);
}
Entrambe le istruzioni di stampa stampano la stessa cosa.
[flat]Map
chiamereste mai la funzione di mappatura con un input == null
? La mia comprensione è che gli Optional
smistamenti se assenti - il [JavaDoc] ( docs.oracle.com/javase/8/docs/api/java/util/… ) sembra supportarlo - " Se è presente un valore, applica .. . ".
Optional.of(null)
è un Exception
. Optional.ofNullable(null) == Optional.empty()
.
Entrambi assumono una funzione dal tipo di opzionale a qualcosa.
map()
applica la funzione "così com'è " sull'opzionale che hai:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
Cosa succede se la tua funzione è una funzione di T -> Optional<U>
?
Il tuo risultato è ora unOptional<Optional<U>>
!
Ecco di cosa flatMap()
si tratta: se la tua funzione restituisce già una Optional
, flatMap()
è un po 'più intelligente e non la avvolge due volte, ritornando Optional<U>
.
È la composizione di due modi di dire funzionali: map
e flatten
.
Nota: - di seguito è illustrata la funzione mappa e flatmap, altrimenti Opzionale è progettato principalmente per essere utilizzato solo come tipo di ritorno.
Come già saprai, Opzionale è un tipo di contenitore che può contenere o meno un singolo oggetto, quindi può essere utilizzato ovunque preveda un valore nullo (potresti non vedere NPE se usi Opzionalmente correttamente). Ad esempio, se hai un metodo che prevede un oggetto persona che potrebbe essere nullable, potresti voler scrivere il metodo in questo modo:
void doSome(Optional<Person> person){
/*and here you want to retrieve some property phone out of person
you may write something like this:
*/
Optional<String> phone = person.map((p)->p.getPhone());
phone.ifPresent((ph)->dial(ph));
}
class Person{
private String phone;
//setter, getters
}
Qui hai restituito un tipo di stringa che viene automaticamente racchiuso in un tipo opzionale.
Se la classe di persona è simile a questa, il telefono è anche opzionale
class Person{
private Optional<String> phone;
//setter,getter
}
In questo caso, invocando la funzione mappa, il valore restituito verrà spostato in Opzionale e produrrà qualcosa del tipo:
Optional<Optional<String>>
//And you may want Optional<String> instead, here comes flatMap
void doSome(Optional<Person> person){
Optional<String> phone = person.flatMap((p)->p.getPhone());
phone.ifPresent((ph)->dial(ph));
}
PS; Non chiamare mai il metodo get (se necessario) su un Opzionale senza verificarlo con isPresent () a meno che non si possa vivere senza NullPointerExceptions.
Person
sta abusando Optional
. È contro l'intenzione dell'API di utilizzare Optional
membri come questo - vedi mail.openjdk.java.net/pipermail/jdk8-dev/2013-Settembre/…
Ciò che mi ha aiutato è stato uno sguardo al codice sorgente delle due funzioni.
Mappa : racchiude il risultato in un Opzionale.
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value)); //<--- wraps in an optional
}
}
flatMap - restituisce l'oggetto 'grezzo'
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value)); //<--- returns 'raw' object
}
}
flatMap
"restituisce l'oggetto 'grezzo'"? flatMap
restituisce anche l'oggetto mappato "avvolto" in un Optional
. La differenza è che, nel caso di flatMap
, la funzione mapper avvolge l'oggetto mappato Optional
mentre lo map
stesso avvolge l'oggetto Optional
.
Optional.map()
:Prende ogni elemento e se il valore esiste, viene passato alla funzione:
Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);
Ora aggiunto ha uno dei tre valori: true
oppure false
racchiuso in un Opzionale , se optionalValue
presente, o in un Opzionale vuoto in caso contrario.
Se non hai bisogno di elaborare il risultato che puoi semplicemente usare ifPresent()
, non ha valore di ritorno:
optionalValue.ifPresent(results::add);
Optional.flatMap()
:Funziona in modo simile allo stesso metodo dei flussi. Appiattisce il flusso di flussi. Con la differenza che se il valore viene presentato, viene applicato alla funzione. Altrimenti, viene restituito un facoltativo vuoto.
Puoi usarlo per comporre chiamate di funzioni di valore opzionali.
Supponiamo di avere metodi:
public static Optional<Double> inverse(Double x) {
return x == 0 ? Optional.empty() : Optional.of(1 / x);
}
public static Optional<Double> squareRoot(Double x) {
return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}
Quindi puoi calcolare la radice quadrata dell'inverso, come:
Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);
o, se preferisci:
Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);
Se inverse()
o squareRoot()
restituisce Optional.empty()
, il risultato è vuoto.
Optional<Double>
tipo come tipo restituito.
Va bene. Devi solo usare 'flatMap' quando ti trovi di fronte a Optionals nidificati . Ecco l'esempio.
public class Person {
private Optional<Car> optionalCar;
public Optional<Car> getOptionalCar() {
return optionalCar;
}
}
public class Car {
private Optional<Insurance> optionalInsurance;
public Optional<Insurance> getOptionalInsurance() {
return optionalInsurance;
}
}
public class Insurance {
private String name;
public String getName() {
return name;
}
}
public class Test {
// map cannot deal with nested Optionals
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.map(Car::getOptionalInsurance) // ① leads to a Optional<Optional<Insurance>
.map(Insurance::getName); // ②
}
}
Come Stream, la # Mappa opzionale restituirà un valore racchiuso da un Opzionale. Ecco perché otteniamo un opzionale nidificato - Optional<Optional<Insurance>
. E a ②, vogliamo mapparlo come un'istanza assicurativa, ecco come è avvenuta la tragedia. La radice è nidificata Optionals. Se riusciamo a ottenere il valore principale indipendentemente dalle shell, lo faremo. Questo è ciò che fa flatMap.
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.flatMap(Car::getOptionalInsurance)
.map(Insurance::getName);
}
Alla fine, ti ho fortemente raccomandato Java 8 In Action se desideri studiare Java8 in modo sistematico.