什么阻止我返回Map <string,list <string =“”>&gt;作为Map <string,collection <string =“”>&gt;?

时间:2018-06-05 04:08:24

标签: java inheritance polymorphism collectors

以下代码不会编译:

  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分组的valsomeEnum的地图。

2 个答案:

答案 0 :(得分:3)

Map<String, List<String>>不是Map<String, Collection<String>>的子类型,有关详情,请参阅this

您可以将返回类型声明为Map<String, ? extends Collection<String>>

答案 1 :(得分:2)

groupingBymapping的方法签名没有关于下游收集器的结果类型的差异。因此,您最终得到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()))));
}