Se você adicionar importações estáticas para Stream.concat e Stream.of , o primeiro exemplo poderá ser escrito da seguinte maneira:
Stream<Foo> stream = concat(stream1, concat(stream2, of(element)));
A importação de métodos estáticos com nomes genéricos pode resultar em um código que fica difícil de ler e manter ( poluição do namespace ). Portanto, pode ser melhor criar seus próprios métodos estáticos com nomes mais significativos. No entanto, para demonstração, continuarei com esse nome.
public static <T> Stream<T> concat(Stream<? extends T> lhs, Stream<? extends T> rhs) {
return Stream.concat(lhs, rhs);
}
public static <T> Stream<T> concat(Stream<? extends T> lhs, T rhs) {
return Stream.concat(lhs, Stream.of(rhs));
}
Com esses dois métodos estáticos (opcionalmente em combinação com importações estáticas), os dois exemplos podem ser escritos da seguinte maneira:
Stream<Foo> stream = concat(stream1, concat(stream2, element));
Stream<Foo> stream = concat(
concat(stream1.filter(x -> x!=0), stream2).filter(x -> x!=1),
element)
.filter(x -> x!=2);
O código agora é significativamente mais curto. No entanto, concordo que a legibilidade não melhorou. Então, eu tenho outra solução.
Em muitas situações, os coletores podem ser usados para estender a funcionalidade dos fluxos. Com os dois coletores na parte inferior, os dois exemplos podem ser escritos da seguinte maneira:
Stream<Foo> stream = stream1.collect(concat(stream2)).collect(concat(element));
Stream<Foo> stream = stream1
.filter(x -> x!=0)
.collect(concat(stream2))
.filter(x -> x!=1)
.collect(concat(element))
.filter(x -> x!=2);
A única diferença entre a sintaxe desejada e a sintaxe acima é que você deve substituir concat (...) por collect (concat (...)) . Os dois métodos estáticos podem ser implementados da seguinte forma (opcionalmente usado em combinação com importações estáticas):
private static <T,A,R,S> Collector<T,?,S> combine(Collector<T,A,R> collector, Function<? super R, ? extends S> function) {
return Collector.of(
collector.supplier(),
collector.accumulator(),
collector.combiner(),
collector.finisher().andThen(function));
}
public static <T> Collector<T,?,Stream<T>> concat(Stream<? extends T> other) {
return combine(Collectors.toList(),
list -> Stream.concat(list.stream(), other));
}
public static <T> Collector<T,?,Stream<T>> concat(T element) {
return concat(Stream.of(element));
}
Obviamente, há uma desvantagem nesta solução que deve ser mencionada. collect é uma operação final que consome todos os elementos do fluxo. Além disso, o coletor concat cria um ArrayList intermediário cada vez que é usado na cadeia. Ambas as operações podem ter um impacto significativo no comportamento do seu programa. No entanto, se a legibilidade for mais importante que o desempenho , ainda poderá ser uma abordagem muito útil.