Что мешает мне вернуть картукак карта? - PullRequest
0 голосов
/ 05 июня 2018

Следующий код не скомпилируется:

  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.

Ответы [ 2 ]

0 голосов
/ 13 июня 2018

Сигнатуры методов 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.К сожалению, базовая реализация не знает о тривиальности этой функции и будет перебирать значения карты результата, чтобы заменить каждое из них результатом применения этой функции.

Это можно решить с помощью специального расширенияcollector:

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()))));
}
0 голосов
/ 05 июня 2018

Map<String, List<String>> не является подтипом Map<String, Collection<String>>, см. это для получения дополнительной информации.

Вы можете объявить тип возврата как Map<String, ? extends Collection<String>>

...