可以使用 guava 轻松过滤列表或 Iterables 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()
它不应该抛出 aClassCastException
而只是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()
.