Списки или итерации можно легко фильтровать с помощью guavas filter(Iterable<?> unfiltered, Class<T> type)
.Эта операция выполняет две задачи: список фильтруется и , преобразованный в последовательность заданного типа 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>>
может просто возвращать отфильтрованную последовательность, которая может быть пустой, вместо того, чтобы возвращать ноль.Дополнительный фильтр может, наконец, отбросить пустые последовательности, используя Iterables.isEmpty()
.