Qual è la differenza tra questi due metodi: Optional.flatMap()e Optional.map()?
Un esempio sarebbe apprezzato.
Stream#flatMape Optional#flatMap.
Qual è la differenza tra questi due metodi: Optional.flatMap()e Optional.map()?
Un esempio sarebbe apprezzato.
Stream#flatMape Optional#flatMap.
Risposte:
Utilizzare mapse la funzione restituisce l'oggetto necessario o flatMapse 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]Mapchiamereste mai la funzione di mappatura con un input == null? La mia comprensione è che gli Optionalsmistamenti 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: mape 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.
Personsta abusando Optional. È contro l'intenzione dell'API di utilizzare Optionalmembri 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'"? flatMaprestituisce anche l'oggetto mappato "avvolto" in un Optional. La differenza è che, nel caso di flatMap, la funzione mapper avvolge l'oggetto mappato Optionalmentre lo mapstesso 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: trueoppure falseracchiuso in un Opzionale , se optionalValuepresente, 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.