Java Generi c проблема: не может разыграть карту М расширяет карту - PullRequest
4 голосов
/ 17 февраля 2020

При попытке написать код Generi c я столкнулся с проблемой. Конечно, я нашел некоторые обходные пути, но, тем не менее, почему приведенный ниже код не работает?

private static <K, U, M extends Map<K, U>>
Supplier<M> mapSupplier() {
    return  HashMap::new;
}

Это возвращает

Error:(25, 17) java: incompatible types: bad return type in lambda expression
    no instance(s) of type variable(s) K,V exist so that java.util.HashMap<K,V> conforms to M

Обновление: мне нужен этот поставщик карт для создания пользовательский сборщик карт:

public static <E, K, V, M extends Map<K, V>>
Collector<E, ?, M> collectToHashMapWithRandomMerge(BiConsumer<M, E> accumulator) {
    return Collector.of(mapSupplier(),
            accumulator,
            TjiCollectionUtils.randomMapMerger());
}

Вызов Collector.of с HashMap :: new также вызывает ту же ошибку компиляции

В идеале, я не Я не хочу создавать дополнительные параметры метода и просто использовать следующее:

  public static <E, K, V, M extends Map<K, V>>
Collector<E, ?, M> collectToHashMapWithRandomMerge(BiConsumer<M, E> accumulator) {
    return Collector.of(HashMap::new,
            accumulator,
            TjiCollectionUtils.randomMapMerger());
}

Ответ Я закончил:

public static <E, K, V>
Collector<E, ?, Map<K, V>> collectToMapWithRandomMerge(BiConsumer<Map<K, V>, E> accumulator) {
    return Collector.of(HashMap::new,
            accumulator,
            TjiCollectionUtils.randomMapMerger());
}

И он вызывается в следующем путь:

MyCollectionUtils.collectToMapWithRandomMerge(
    (Map<String,Integer> m, SomeClassToExtractFrom e) -> ...);

Ответы [ 2 ]

4 голосов
/ 17 февраля 2020

Похоже, это распространенное заблуждение об универсальных шаблонах, которое заключается в том, что вызываемый объект решает, какие типы являются параметрами универсального c. Фактически, звонящий делает.

Тот, кто звонит mapSupplier, решает, что такое K, U и M. Допустим, я звоню и хочу, чтобы K было Integer, U было бы String, а M было бы Hashtable<Integer, String>. Это верно, потому что Hashtable реализует Map.

Supplier<Hashtable<Integer, String>> htSupplier = mapSupplier();
Hashtable<Integer, String> ht = htSupplier.get();

Как вызывающий объект, я ожидаю, что вышеприведенное сработает, но htSupplier.get с вашей реализацией фактически даст мне HashMap<Integer, String> , который не связан с Hashtable (в терминах иерархии наследования).

Другими словами, mapSupplier единолично решил, что M должно быть HashMap<K, U>, и при этом сказал, что он будет работать на любом M, который реализует Map<K, U>.

* 1031. * Всякий раз, когда вы видите, что пишете метод "generi c", который решает, каковы его параметры generi c, этот метод, вероятно, не должен иметь этот параметр generi c. Поэтому mapSupplier, вероятно, следует переписать без параметра M:
private static <K, U>
Supplier<HashMap<K, U>> mapSupplier() {
    return  HashMap::new;
}

РЕДАКТИРОВАТЬ:

Видя вызывающего абонента, я думаю, вы можете либо:

  • также удалите M из collectToHashMapWithRandomMerge или:
  • заставьте collectToHashMapWithRandomMerge принять Supplier<M>, чтобы его вызывающий мог определить тип карты.
3 голосов
/ 17 февраля 2020

M extends Map<K, V> означает специфицированный c класс, который расширяет Map. HashMap - это класс, который расширяет Map, но M может быть, скажем, LinkedHashMap; HashMap не является LinkedHashMap.

Вы, похоже, уже получили Supplier<M> в качестве параметра:

public static <E, K, V, M extends Map<K, V>>
Collector<E, ?, M> collectToHashMapWithRandomMerge(Supplier<M> mapSupplier /* HERE */, BiConsumer<M, E> accumulator) {
    return Collector.of(mapSupplier(),
            accumulator,
            TjiCollectionUtils.randomMapMerger());
}

Итак, просто используйте mapSupplier ( параметр) вместо mapSupplier() (результат вызова метода).

...