无法推断Collectors.toMap的类型变量

5

我正在尝试创建一个EnumMap,它将枚举常量简单地映射到其各自的序数。我想我可以这样简洁地完成:

private static Map<Rank, Integer> defaultRankOrdering = new EnumMap<>(
    Arrays.stream(Rank.values()).collect(
        Collectors.toMap(Function::identity, e -> e.ordinal())));

然而,编译器说它不能推断出toMap的任何类型变量。我不明白为什么,因为根据toMap的两个参数的签名:

public static <T, K ,U> Collector<T, ? ,Map<K, U>> toMap(Function<? super T, ? extends K> keyMapper,
                                                         Function<? super T, ? extends U> valueMapper)

T 应被推断为 Rank,因为 Arrays.stream(Rank.values()) 是一个 Stream<Rank>K 应该也被推断为 Rank,因为 Function.identity 返回一个 Function<T, T>U 应被推断为 Integer,因为 Enum.ordinal 的返回类型是 int,在这种情况下应该将其装箱为 Integer

非常感谢您的帮助。

2个回答

6
你需要的是Function.identity(),而不是Function::identity
private static Map<Rank, Integer> defaultRankOrdering = new EnumMap<>(
    Arrays.stream(Rank.values()).collect(
        Collectors.toMap(Function.identity(), e -> e.ordinal())));
Function::identity 的类型是 Supplier<Function<T,T>>,因为它是一个不接受参数并返回 Function<T,T> 的方法引用。

你需要的键映射函数是一个 Function<T,T>,只需调用方法 Function.identity() 即可返回。


这很有道理。我对Java还很陌生,像这样的错误仍然很难发现。 - Isaac Saffold

3
即使@Eran已经指出了您的问题,但我认为您需要这段代码片段,因此将其写入我的答案中:
static Map<Rank, Integer>
defaultRankOrdering = Arrays.stream(Rank.values()).collect(toMap(
        Function.identity(),
        Enum::ordinal,
        (v1, v2) -> v1, // it'is never be used, only support for create an EnumMap
        () -> new EnumMap<>(Rank.class)
));

我曾考虑使用四个参数的方法,但出于某种奇怪的原因,我认为第三个参数(从未使用)比使用EnumMap<K, V>(Map<K, V> map)构造函数更浪费。现在我想了想,显然是相反的。 - Isaac Saffold
2
@IsaacSaffold 你好,第三个参数是用于合并具有相同键的值,但对于 Enum.values() 来说,没有重复的键,所以你可以简单地处理它,并且它永远不会被调用。看起来需要写更多的代码,但使用四个参数的 toMap 算法复杂度为 O(N),而你的算法复杂度为 O(2N) - holi-java
对,我一定累了。 - Isaac Saffold

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接