Qual é a diferença entre esses dois métodos: Optional.flatMap()
e Optional.map()
?
Um exemplo seria apreciado.
Stream#flatMap
e Optional#flatMap
.
Qual é a diferença entre esses dois métodos: Optional.flatMap()
e Optional.map()
?
Um exemplo seria apreciado.
Stream#flatMap
e Optional#flatMap
.
Respostas:
Use map
se a função retornar o objeto que você precisa ou flatMap
se a função retornar um Optional
. Por exemplo:
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);
}
As duas instruções print imprimem a mesma coisa.
[flat]Map
alguma vez chamaria a função de mapeamento com um input == null
? Meu entendimento é que os Optional
ordenamentos se estiverem ausentes - o [JavaDoc] ( docs.oracle.com/javase/8/docs/api/java/util/… ) parece fazer o backup - " Se houver um valor presente, aplique .. . "
Optional.of(null)
é um Exception
. Optional.ofNullable(null) == Optional.empty()
.
Ambos assumem uma função do tipo do opcional para algo.
map()
aplica a função " como está " no opcional que você possui:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
O que acontece se sua função for uma função T -> Optional<U>
?
O seu resultado é agora um Optional<Optional<U>>
!
É disso que flatMap()
se trata: se sua função já retorna um Optional
, flatMap()
é um pouco mais inteligente e não o envolve duas vezes, retornando Optional<U>
.
É a composição de dois idiomas funcionais: map
e flatten
.
Nota: - abaixo está a ilustração da função mapa e mapa plano, caso contrário, o opcional é projetado principalmente para ser usado apenas como um tipo de retorno.
Como você já deve saber, Opcional é um tipo de contêiner que pode ou não conter um único objeto; portanto, ele pode ser usado sempre que você antecipar um valor nulo (você nunca verá o NPE se usar o Opcional corretamente). Por exemplo, se você possui um método que espera um objeto pessoal que possa ser anulável, você pode escrever o método da seguinte forma:
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
}
Aqui você retornou um tipo de String que é automaticamente agrupado em um tipo opcional.
Se a classe da pessoa tiver essa aparência, o telefone também é opcional
class Person{
private Optional<String> phone;
//setter,getter
}
Nesse caso, invocar a função map envolverá o valor retornado em Opcional e produzirá algo como:
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; Nunca chame o método get (se necessário) em um Opcional sem verificar com isPresent (), a menos que você não possa viver sem NullPointerExceptions.
Person
está sendo mal utilizada Optional
. É contra a intenção da API usar Optional
membros como este - consulte mail.openjdk.java.net/pipermail/jdk8-dev/2013-Setembro/…
O que me ajudou foi uma olhada no código fonte das duas funções.
Mapa - agrupa o resultado em um opcional.
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 - retorna o objeto 'bruto'
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
"retorna o objeto 'bruto'"? flatMap
também retorna o objeto mapeado "empacotado" em um Optional
. A diferença é que, no caso de flatMap
, a função mapeador envolve o objeto mapeado no Optional
momento em que map
ele envolve o objeto Optional
.
Optional.map()
:Pega todos os elementos e, se o valor existe, é passado para a função:
Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);
Agora adicionado possui um dos três valores: true
ou false
agrupado em um Opcional , se optionalValue
presente, ou um Opcional vazio, caso contrário.
Se você não precisar processar o resultado que pode usar ifPresent()
, ele não terá valor de retorno:
optionalValue.ifPresent(results::add);
Optional.flatMap()
:Funciona de maneira semelhante ao mesmo método de fluxos. Nivela o fluxo de fluxos. Com a diferença de que, se o valor for apresentado, ele será aplicado à função. Caso contrário, um opcional vazio será retornado.
Você pode usá-lo para compor chamadas de funções de valor opcionais.
Suponha que tenhamos métodos:
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));
}
Então você pode calcular a raiz quadrada do inverso, como:
Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);
ou, se você preferir:
Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);
Se o inverse()
ou os squareRoot()
retornos Optional.empty()
, o resultado está vazio.
Optional<Double>
tipo como tipo de retorno.
OK. Você só precisa usar 'flatMap' quando estiver enfrentando opcionais aninhados . Aqui está o exemplo.
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); // ②
}
}
Como o Stream, o mapa # opcional retornará um valor agrupado por um opcional. É por isso que obtemos um Opcional aninhado - Optional<Optional<Insurance>
. E em ②, queremos mapeá-lo como uma instância de seguro, foi assim que a tragédia aconteceu. A raiz está aninhada opcional. Se conseguirmos obter o valor principal, independentemente das conchas, faremos isso. É isso que o flatMap faz.
public Optional<String> getCarInsuranceName(Person person) {
return person.getOptionalCar()
.flatMap(Car::getOptionalInsurance)
.map(Insurance::getName);
}
No final, eu recomendo fortemente o Java 8 In Action para você, se você gostaria de estudar o Java8 sistematicamente.