Estou adicionando esta segunda resposta com base em uma edição proposta pelo usuário srborlongan à minha outra resposta . Eu acho que a técnica proposta foi interessante, mas não foi realmente adequada como uma edição da minha resposta. Outros concordaram e a edição proposta foi rejeitada. (Eu não era um dos eleitores.) A técnica tem mérito, no entanto. Teria sido melhor se srborlongan tivesse postado sua própria resposta. Isso ainda não aconteceu, e eu não queria que a técnica fosse perdida nas brumas do histórico de edição rejeitado do StackOverflow, então decidi apresentá-la como uma resposta separada.
Basicamente, a técnica é usar alguns dos Optional
métodos de maneira inteligente para evitar a necessidade de usar um operador ternário ( ? :
) ou uma declaração if / else.
Meu exemplo embutido seria reescrito desta maneira:
Optional<Other> result =
things.stream()
.map(this::resolve)
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
.findFirst();
Um exemplo meu que usa um método auxiliar seria reescrito desta maneira:
/**
* Turns an Optional<T> into a Stream<T> of length zero or one depending upon
* whether a value is present.
*/
static <T> Stream<T> streamopt(Optional<T> opt) {
return opt.map(Stream::of)
.orElseGet(Stream::empty);
}
Optional<Other> result =
things.stream()
.flatMap(t -> streamopt(resolve(t)))
.findFirst();
COMENTÁRIO
Vamos comparar as versões originais vs modificadas diretamente:
// original
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
// modified
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
O original é uma abordagem direta, se for de trabalhador: obtemos um Optional<Other>
; se tiver um valor, retornamos um fluxo contendo esse valor e, se não tiver valor, retornamos um fluxo vazio. Muito simples e fácil de explicar.
A modificação é inteligente e tem a vantagem de evitar condicionais. (Eu sei que algumas pessoas não gostam do operador ternário. Se mal utilizado, pode realmente dificultar a compreensão do código.) No entanto, às vezes as coisas podem ser muito inteligentes. O código modificado também começa com um Optional<Other>
. Em seguida, chama o Optional.map
que é definido da seguinte forma:
Se um valor estiver presente, aplique a função de mapeamento fornecida a ele e, se o resultado for não nulo, retorne um Opcional descrevendo o resultado. Caso contrário, retorne um opcional opcional.
A map(Stream::of)
chamada retorna um Optional<Stream<Other>>
. Se um valor estava presente na entrada Opcional, o Opcional retornado contém um Fluxo que contém o único Outro resultado. Mas se o valor não estava presente, o resultado é um opcional vazio.
Em seguida, a chamada para orElseGet(Stream::empty)
retorna um valor de tipo Stream<Other>
. Se o seu valor de entrada estiver presente, ele obtém o valor, que é o elemento único Stream<Other>
. Caso contrário (se o valor de entrada estiver ausente), ele retornará um vazio Stream<Other>
. Portanto, o resultado está correto, o mesmo que o código condicional original.
Nos comentários discutindo minha resposta, sobre a edição rejeitada, eu descrevi essa técnica como "mais concisa, mas também mais obscura". Eu mantenho isso. Demorei um pouco para descobrir o que estava fazendo e também demorei um pouco para escrever a descrição acima do que estava fazendo. A sutileza principal é a transformação de Optional<Other>
para Optional<Stream<Other>>
. Uma vez que você grunhe, faz sentido, mas não foi óbvio para mim.
Reconhecerei, porém, que coisas inicialmente obscuras podem se tornar idiomáticas ao longo do tempo. Pode ser que essa técnica acabe sendo a melhor maneira na prática, pelo menos até Optional.stream
ser adicionada (se for o caso).
UPDATE: Optional.stream
foi adicionado ao JDK 9.
.flatMap(Optional::toStream)
, à sua versão e você realmente vê o que está acontecendo.