以下代码不会编译:
public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
return foos == null ? Maps.newHashMap()
: foos.stream().collect(
Collectors.groupingBy(
f -> f.getSomeEnum().name(),
Collectors.mapping(Foo::getVal, Collectors.toList())));
}
除非我将返回类型更改为List
:
public static Map<String, List<String>> convert(Collection<Foo> foos) {
通常我相信我应该可以,但也许仿制药引入了一些歧义?
确切错误:
Incompatible types. Required Map<String, Collection<String>> but 'collect' was inferred to R: no instance(s) of type variable(s) A, A, A, A, A, A, D, K, R, R, T, T, T, U exist so that List<T> conforms to Collection<String>
我不认为细节是相关的,但以防万一:
Foo
是这样的:
public class Foo {
private MyEnumType someEnum;
private String val;
public MyEnumType getSomeEnum();
public String getVal();
}
我正在尝试将Foo
的列表转换为按Foo
分组的val
,someEnum
的地图。
答案 0 :(得分:3)
Map<String, List<String>>
不是Map<String, Collection<String>>
的子类型,有关详情,请参阅this。
您可以将返回类型声明为Map<String, ? extends Collection<String>>
答案 1 :(得分:2)
groupingBy
和mapping
的方法签名没有关于下游收集器的结果类型的差异。因此,您最终得到List<T>
收集器的结果类型toList()
。相比之下,toCollection
的类型参数C extends Collection<T>
存在差异,但即使没有,将ArrayList::new
分配给Supplier<Collection<…>>
也不会有任何问题:
public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
return foos == null? new HashMap<>(): foos.stream().collect(
Collectors.groupingBy(f -> f.getSomeEnum().name(),
Collectors.mapping(Foo::getVal, Collectors.toCollection(ArrayList::new))));
}
考虑到当前的实现,这与toList()
完全相同。但它不会受益于专门针对toList()
收集器的未来改进。另一种方法是继续使用toList()
,但链接类型转换:
public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
return foos == null? new HashMap<>(): foos.stream().collect(
Collectors.groupingBy(f -> f.getSomeEnum().name(),
Collectors.collectingAndThen(
Collectors.mapping(Foo::getVal, Collectors.toList()),
c -> c)));
}
由于这是一个扩展转换,转换功能就像c -> c
一样简单。不幸的是,底层实现并不知道这个函数的微不足道,并将迭代结果映射的值,用应用此函数的结果替换它们中的每一个。
这可以通过特殊的扩展收集器来解决:
public static <T,A,R extends W,W> Collector<T,A,W> wideningResult(Collector<T,A,R> original) {
return Collector.of(original.supplier(), original.accumulator(), original.combiner(),
original.finisher().andThen(t -> t),
original.characteristics().toArray(new Collector.Characteristics[0]));
}
这与Collectors.collectingAndThen(original, t -> t)
基本相同,链接了普通的加宽转换函数。但它保留了原始收集器的特性,因此如果原始收集器具有IDENTITY_FINISH
特性,我们仍然会拥有它,这允许跳过整理操作,如果groupingBy
意味着它没有'需要迭代地图以应用该功能。
将其应用于实际用例会产生
public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
return foos == null? new HashMap<>(): foos.stream().collect(
Collectors.groupingBy(f -> f.getSomeEnum().name(),
wideningResult(Collectors.mapping(Foo::getVal, Collectors.toList()))));
}