Si agrega importaciones estáticas para Stream.concat y Stream.of , el primer ejemplo podría escribirse de la siguiente manera:
Stream<Foo> stream = concat(stream1, concat(stream2, of(element)));
La importación de métodos estáticos con nombres genéricos puede generar un código que se vuelve difícil de leer y mantener ( contaminación del espacio de nombres ). Por lo tanto, podría ser mejor crear sus propios métodos estáticos con nombres más significativos. Sin embargo, para la demostración me quedaré con este nombre.
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));
}
Con estos dos métodos estáticos (opcionalmente en combinación con importaciones estáticas), los dos ejemplos podrían escribirse de la siguiente manera:
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);
El código ahora es significativamente más corto. Sin embargo, estoy de acuerdo en que la legibilidad no ha mejorado. Entonces tengo otra solución.
En muchas situaciones, los recopiladores se pueden usar para ampliar la funcionalidad de las transmisiones. Con los dos recopiladores en la parte inferior, los dos ejemplos podrían escribirse de la siguiente manera:
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);
La única diferencia entre la sintaxis deseada y la sintaxis anterior es que debe reemplazar concat (...) con collect (concat (...)) . Los dos métodos estáticos se pueden implementar de la siguiente manera (opcionalmente en combinación con importaciones 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));
}
Por supuesto, hay una desventaja con esta solución que debería mencionarse. recoger es una operación final que consume todos los elementos de la secuencia. Además de eso, el recopilador concat crea una ArrayList intermedia cada vez que se usa en la cadena. Ambas operaciones pueden tener un impacto significativo en el comportamiento de su programa. Sin embargo, si la legibilidad es más importante que el rendimiento , aún podría ser un enfoque muy útil.