【发布时间】:2011-11-04 12:00:31
【问题描述】:
可以使用 guava filter(Iterable<?> unfiltered, Class<T> type) 轻松过滤列表或 Iterables。此操作执行两个任务:过滤列表和转换为给定类型 T 的序列。
但我经常得到Iterables<Something<?>>
我想为一些专门的T获得Iterables<Something<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()); -
为什么要一步到位?转换和过滤是不同的操作。
标签: java generics guava iterable