@AlexisC. Seu link é sobre o mapa e o flatMap do Stream, não opcional.
Eran
1
@ Eran Isso não importa, se você entende como o map / flatMap funciona, seja para um stream ou não, é o mesmo para um opcional. Se o op entendeu como funciona para um Stream, ele não deveria fazer essa pergunta. O conceito é o mesmo.
Alexis C.
2
@AlexisC. Na verdade não. O flatMap opcional tem pouco em comum com o flatMap do Stream.
Eran
1
@Eran Estou falando sobre a diferença conceitual entre um mapa e um flatMap, não estou fazendo uma correspondência individual entre Stream#flatMape Optional#flatMap.
Alexis C.
Respostas:
166
Use mapse a função retornar o objeto que você precisa ou flatMapse a função retornar um Optional. Por exemplo:
publicstaticvoid main(String[] args){Optional<String> s =Optional.of("input");System.out.println(s.map(Test::getOutput));System.out.println(s.flatMap(Test::getOutputOpt));}staticString getOutput(String input){return input ==null?null:"output for "+ input;}staticOptional<String> getOutputOpt(String input){return input ==null?Optional.empty():Optional.of("output for "+ input);}
Pergunta: [flat]Mapalguma vez chamaria a função de mapeamento com um input == null? Meu entendimento é que os Optionalordenamentos se estiverem ausentes - o [JavaDoc] ( docs.oracle.com/javase/8/docs/api/java/util/… ) parece fazer o backup - " Se houver um valor presente, aplique .. . "
@DiegoMartinoia Optional.of(null)é um Exception. Optional.ofNullable(null) == Optional.empty().
Boris the Spider
1
@BoristheSpider sim, você está certo. Eu estava tentando responder à sua pergunta, mas acho que a tornei ainda mais clara: conceitualmente, Optional.ofNullable (null) NÃO deve estar vazio, mas na prática é considerado, e, portanto, map / flatmap não é executado.
Diego Martinoia
1
Acho entrada nunca deve ser nula em qualquer getOutputOpt ou getOutput
DanyalBurke
55
Ambos assumem uma função do tipo do opcional para algo.
map()aplica a função " como está " no opcional que você possui:
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: mape 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));}classPerson{privateString 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
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 flatMapvoid 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.
Acho que este exemplo provavelmente distrairá a natureza da sua resposta, porque sua classe Personestá sendo mal utilizada Optional. É contra a intenção da API usar Optionalmembros como este - consulte mail.openjdk.java.net/pipermail/jdk8-dev/2013-Setembro/…
8bitjunkie
@ 8bitjunkie Obrigado por apontar isso, difere da opção do Scala .. #
SandeepGodara 21/17/17
6
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{returnOptional.ofNullable(mapper.apply(value));//<--- wraps in an optional}}
O que você quer dizer com flatMap"retorna o objeto 'bruto'"? flatMaptambé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 Optionalmomento em que mapele envolve o objeto Optional.
Derek Mahar
O @DerekMahar excluiu o meu, não há necessidade de republicá-lo, porque você editou seu comentário corretamente.
maxxyme
3
Optional.map():
Pega todos os elementos e, se o valor existe, é passado para a função:
Agora adicionado possui um dos três valores: trueou falseagrupado em um Opcional , se optionalValuepresente, 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:
publicstaticOptional<Double> inverse(Double x){return x ==0?Optional.empty():Optional.of(1/ x);}publicstaticOptional<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.
Isso não compila. Ambas as suas expressões retornam um <Duplo> opcional em vez do Duplo ao qual você está atribuindo o resultado.
JL_SO 5/06/19
@JL_SO você está certo. Porque inverso tem Optional<Double>tipo como tipo de retorno.
nazar_art
3
OK. Você só precisa usar 'flatMap' quando estiver enfrentando opcionais aninhados . Aqui está o exemplo.
publicclassPerson{privateOptional<Car> optionalCar;publicOptional<Car> getOptionalCar(){return optionalCar;}}publicclassCar{privateOptional<Insurance> optionalInsurance;publicOptional<Insurance> getOptionalInsurance(){return optionalInsurance;}}publicclassInsurance{privateString name;publicString getName(){return name;}}publicclassTest{// map cannot deal with nested OptionalspublicOptional<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.
Stream#flatMap
eOptional#flatMap
.Respostas:
Use
map
se a função retornar o objeto que você precisa ouflatMap
se a função retornar umOptional
. Por exemplo:As duas instruções print imprimem a mesma coisa.
fonte
[flat]Map
alguma vez chamaria a função de mapeamento com uminput == null
? Meu entendimento é que osOptional
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)
é umException
.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: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 umOptional
,flatMap()
é um pouco mais inteligente e não o envolve duas vezes, retornandoOptional<U>
.É a composição de dois idiomas funcionais:
map
eflatten
.fonte
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:
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
Nesse caso, invocar a função map envolverá o valor retornado em Opcional e produzirá algo como:
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.
fonte
Person
está sendo mal utilizadaOptional
. É contra a intenção da API usarOptional
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.
flatMap - retorna o objeto 'bruto'
fonte
flatMap
"retorna o objeto 'bruto'"?flatMap
também retorna o objeto mapeado "empacotado" em umOptional
. A diferença é que, no caso deflatMap
, a função mapeador envolve o objeto mapeado noOptional
momento em quemap
ele envolve o objetoOptional
.Optional.map()
:Pega todos os elementos e, se o valor existe, é passado para a função:
Agora adicionado possui um dos três valores:
true
oufalse
agrupado em um Opcional , seoptionalValue
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: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:
Então você pode calcular a raiz quadrada do inverso, como:
ou, se você preferir:
Se o
inverse()
ou ossquareRoot()
retornosOptional.empty()
, o resultado está vazio.fonte
Optional<Double>
tipo como tipo de retorno.OK. Você só precisa usar 'flatMap' quando estiver enfrentando opcionais aninhados . Aqui está o exemplo.
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.No final, eu recomendo fortemente o Java 8 In Action para você, se você gostaria de estudar o Java8 sistematicamente.
fonte