Списки или Iterables можно легко фильтровать с помощью guavas _ 1_. Эта операция выполняет две задачи: список фильтруется и преобразуется в последовательность заданного типа T.
Однако довольно часто я получаю Iterables<Something<?>>
и хочу получить подпоследовательность Iterables<Something<T>>
для некоторого специализированного T.
Понятно, что Guava не может решить эту проблему из коробки из-за стирания типа: Something<T>
не предоставляет никакой прямой информации о своем T.
Допустим, у меня есть что-то вроде S<? extends Number>
. Если я могу определить какой-то предикат, который сообщает мне, может ли S<?>
быть преобразован в S<Double>
, я могу использовать его как фильтр:
<T extends Number> Predicate<S<?>> isOfType(Class<N> type) {...}
с участием:
Iterable<S<?>> numbers;
Iterable<S<?>> filtered = Iterable.filter(numbers, isOfType(Double.class));
Это выполняет задачу фильтрации, но пропускает этап преобразования. Если я думаю, что мой предикат работает хорошо, я могу даже подумать о кастинге:
Iterable<S<Double>> doubles = (Iterable<S<Double>>) filtered;
Но это показывает некоторую уродливую операцию приведения.
В качестве альтернативы я могу предоставить Function<S<?>, S<Double>>
для выполнения приведения. В отличие от Class.cast()
, однако он не должен выдавать ClassCastException
, а просто возвращать null
, если элемент не может быть приведен (или преобразован). Таким образом, последовательность может быть преобразована без явного приведения:
<T extends Number> Function<S<?>, S<T>> castOrNull(Class<N> type) {...}
Iterable<S<Double>> doubles = Iterable.filter(numbers, castOrNull(Double.class));
Но на самом деле список не фильтруется: вместо этого он по-прежнему содержит нулевые объекты для каждого элемента, который не может быть преобразован или преобразован в S<Double>
. Но это можно легко решить с помощью дополнительной фильтрации, например:
Iterable<S<Double>> doubles = Iterables.filter(doubles, Predicates.notNull());
Мне кажется, что второе решение намного умнее. Определяемый Function
может либо выполнить приведение (которое скрывает непроверенную операцию), либо действительно создать новый объект S<T>
, если это необходимо.
Остается вопрос: есть ли более разумный способ выполнить необходимое преобразование и фильтрацию за один шаг? Я могу просто определить некоторую служебную функцию, например:
<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);
Где вторая функция является сокращением первой с Predicates.notNull()
;
Но стоит иметь и первую функцию, так как предикат не нужен Predicates.notNull()
.
Представьте себе Iterable<Iterable<? extends Number>>
. Функция преобразователя Function<Iterable<? extends Number>, Iterable<Double>>
может просто возвращать отфильтрованную последовательность, которая может быть пустой, вместо того, чтобы возвращать значение NULL. Дополнительный фильтр может окончательно отбросить пустые последовательности, используя Iterables.isEmpty()
.
Iterable.filter(...)
возвращал итерацию с расширенными функциями, чтобы вы могли связать фильтры./* S extends Collection */ Iterable<S<Double>> doubles = Iterable.filter(numbers, castOrNull(Double.class)).filter(Predicates.notNull()).filter(Predicates.notEmpty());
- person aalku   schedule 05.09.2011