Las listas o Iterables se pueden filtrar fácilmente usando guavas filter(Iterable<?> unfiltered, Class<T> type)
. Esta operación realiza dos tareas: la lista se filtra y transforma en una secuencia de la T. tipo dadoFiltrando listas de tipos genéricos
Muy a menudo, sin embargo termino con Iterables<Something<?>>
y quiero obtener una subsecuencia de Iterables<Something<T>>
por alguna T. especializada
es evidente, que la guayaba no puede resolver este problema fuera de la caja debido al tipo de borrado: Something<T>
no proporciona ninguna información directa acerca de su T.
Digamos que tengo algo así como S<? extends Number>
. Si soy capaz de definir algún predicado que me dice si S<?>
puede ser moldeado a S<Double>
que puede usarlo como un archivador:
<T extends Number> Predicate<S<?>> isOfType(Class<N> type) {...}
con:
Iterable<S<?>> numbers;
Iterable<S<?>> filtered = Iterable.filter(numbers, isOfType(Double.class));
Este realiza la tarea de filtrado, pero pierde el paso de transformación. Si pienso en mi predicado funciona bien puedo siquiera pensar en la fundición:
Iterable<S<Double>> doubles = (Iterable<S<Double>>) filtered;
Pero esto expone a alguna operación de fundición feo.
Como alternativa, puedo proporcionar un Function<S<?>, S<Double>>
para realizar el reparto. En contraste con Class.cast()
, sin embargo, no debería arrojar un ClassCastException
, sino que simplemente devuelve null
si el elemento no se puede convertir (o transformar). De esta manera la secuencia se puede convertir sin ningún tipo de conversión explícita:
<T extends Number> Function<S<?>, S<T>> castOrNull(Class<N> type) {...}
Iterable<S<Double>> doubles = Iterable.filter(numbers, castOrNull(Double.class));
Pero la lista no está realmente filtrada: en lugar todavía contiene objetos nulos para cada elemento que no pudo convertir o fundido a S<Double>
. Pero esto puede resolverse fácilmente mediante una etapa de filtración adicional como:
Iterable<S<Double>> doubles = Iterables.filter(doubles, Predicates.notNull());
La segunda solución parece mucho más inteligente que yo. El Function
que se va a definir puede realizar una conversión (que oculta la operación no seleccionada) o puede realmente crear un nuevo objeto S<T>
si es necesario.
La pregunta restante es: ¿Existe alguna forma más inteligente de realizar la conversión y el filtrado necesarios en un solo paso? Puede que simplemente definir algunas función de utilidad como:
<I,O> Iterables<O> convert(
Iterables<O> input,
Function<? super I, ? extends O> convert,
Predicate<? super O> filter);
<I,O> Iterables<O> convert(
Iterables<O> input,
Function<? super I, ? extends O> convert);
Cuando la segunda función es un corte corto de la primera con una Predicates.notNull()
;
Pero también vale la pena tener la primera función, ya que el predicado no es necesario Predicates.notNull()
.
Imagine un Iterable<Iterable<? extends Number>>
. La función del convertidor Function<Iterable<? extends Number>, Iterable<Double>>
puede simplemente devolver una secuencia filtrada que puede estar vacía en lugar de devolver nula. El filtro adicional finalmente puede soltar secuencias vacías usando Iterables.isEmpty()
.
Sería útil si 'Iterable.filter (...)' devuelve un iterable con funcionalidad extendida para que pueda encadenar filtros. '/ * S extends Collection */Iterable
> double = Iterable.filter (numbers, castOrNull (Double.class)). Filter (Predicates.notNull()). Filter (Predicates.notEmpty());' – aalku¿Por qué? ¿Quieres hacerlo en un solo paso? La transformación y el filtrado son operaciones distintas. – pawstrong