为什么这个代码无法编译,引用类型推断作为原因?

问题描述 投票:11回答:2

这是我正在使用的代码的最小示例:

public class Temp {
    enum SomeEnum {}

    private static final Map<SomeEnum, String> TEST = new EnumMap<>(
               Arrays.stream(SomeEnum.values())
                     .collect(Collectors.toMap(t -> t, a -> "")));

}

编译器输出是:

Temp.java:27: error: cannot infer type arguments for EnumMap<>
    private static final Map<SomeEnum, String> TEST = new EnumMap<>(Arrays.stream(SomeEnum.values())
                                                      ^

我发现这可以通过用t -> tFunction.identity()替换(SomeEnum t) -> t来解决,但我不明白为什么会这样。 javac有什么限制导致这种行为?

我最初在java 8中发现了这个问题,但已经证实它仍然发生在java 11编译器中。

java generics java-8 javac type-inference
2个回答
9
投票

我们可以进一步简化示例:

声明一个类似的方法

static <K,V> Map<K,V> test(Map<K,? extends V> m) {
    return Collections.unmodifiableMap(m);
}

该声明

Map<SomeEnum, String> m = test(Collections.emptyMap());

可以编译没有问题。现在,当我们将方法声明更改为

static <K extends Enum<K>,V> Map<K,V> test(Map<K,? extends V> m) {
    return Collections.unmodifiableMap(m);
}

我们得到编译器错误。这表明使用new EnumMap<>(…)new HashMap<>(…)包装流表达式之间的区别在于键类型的类型参数声明,因为EnumMap的键类型参数已声明为K extends Enum<K>

它似乎与宣言的自我指涉性有关,例如: K extends Serializable不会导致K extends Comparable<K>错误。

虽然从Java 8到Java 11的所有javac版本都失败了,但行为并不像看起来那么一致。当我们将声明更改为

static <K extends Enum<K>,V> Map<K,V> test(Map<? extends K,? extends V> m) {
    return Collections.unmodifiableMap(m);
}

代码可以在Java 8下再次编译,但仍然在Java 9到11中失败。

对我而言,编译器为SomeEnum(可以匹配绑定的K)和Enum<K>String推断V是不合逻辑的,但是当为K指定了绑定时未能推断出这些类型。所以我认为这是一个错误。我不能排除在规范深度的某处有一个声明,它允许得出结论编译器应该以这种方式运行,但如果是这样,那么规范也应该被修复。

正如评论部分中的其他人所说,这个代码可以用Eclipse编译而没有问题。


-2
投票

如果我们明确提到类型而不是使用菱形运算符,那么它会成功编译。以下是相同的代码:

private static final Map<SomeEnum, String> TEST = new EnumMap<SomeEnum, String>(
            Arrays.stream(SomeEnum.values())
                    .collect(Collectors.toMap(t -> t, a -> "")));

根据另一个link的参考,在某些情况下,不支持钻石运算符。如果这里讨论的代码片落在这个桶中,可以进一步挖掘它。

© www.soinside.com 2019 - 2024. All rights reserved.