Java 一般问题:无法将 Map<K,V> 转换为 M extends Map<K, V>

Java Generic issue: cannot cast Map<K,V> to M extends Map<K, V>

在尝试编写一些通用代码时遇到了问题。当然,我找到了一些解决方法,但为什么下面的代码不起作用?

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

这个returns

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());
}

HashMap::new调用Collector.of也会导致同样的编译错误

理想情况下,我不想创建额外的方法参数,只需使用以下内容:

  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) -> ...);

M extends Map<K, V> 表示扩展 Map 特定 class。 HashMap 是扩展 Map 的 class,但是 M 可以是 LinkedHashMapHashMap 不是 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()(调用方法的结果)。

这似乎是对泛型的一个普遍误解,即由被调用者决定泛型参数是什么类型。事实上,来电者确实如此。

谁调用 mapSupplier 就可以决定 KUM 是什么。假设我正在调用它,我希望 KIntegerUStringMHashtable<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>,同时还说它将适用于任何实现 Map<K, U>.

每当您看到自己编写一个 "generic" 方法来决定其通用参数是什么时,该方法可能不应该具有该通用参数。因此, mapSupplier 应该可以在没有 M 参数的情况下重写:

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

编辑:

看到来电者,我想你可以:

  • 同时从 collectToHashMapWithRandomMerge 中删除 M,或者:
  • collectToHashMapWithRandomMerge接受一个Supplier<M>,这样它的调用者可以决定地图的类型。