这是我正在使用的代码的最小示例:
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 -> t
替换为Function.identity()
或(SomeEnum t) -> t
来解决此问题,但是我不明白为什么会这样。 javac中的哪些限制导致了此行为?
我最初在Java 8中发现了此问题,但已验证在Java 11编译器中仍然会发生。
答案 0 :(得分: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
的键类型参数已声明为{{ 1}}。
它似乎与声明的自我引用性质有关,例如K extends Enum<K>
不会引起错误。
虽然从Java 8到Java 11的所有K extends Serializable
版本中此操作均失败,但其行为并不像看起来那样一致。当我们将声明更改为
K extends Comparable<K>
可以在Java 8下再次编译该代码,但是对于Java 9到11仍然失败。
对我来说,编译器推断javac
的{{1}}(将与绑定的static <K extends Enum<K>,V> Map<K,V> test(Map<? extends K,? extends V> m) {
return Collections.unmodifiableMap(m);
}
匹配)和SomeEnum
推断K
是不合逻辑的,但是失败在为Enum<K>
指定界限时推断这些类型。 所以我认为这是一个错误。我不能排除在规范的深处有一条语句可以得出结论,即编译器应该以这种方式运行,但是如果是这样,则规范也应该得到固定。
正如其他人在评论部分中所述,可以使用Eclipse编译此代码而不会出现问题。
答案 1 :(得分:-2)
如果我们明确提及类型而不是使用菱形运算符,则它将成功编译。以下是相同的代码:
private static final Map<SomeEnum, String> TEST = new EnumMap<SomeEnum, String>(
Arrays.stream(SomeEnum.values())
.collect(Collectors.toMap(t -> t, a -> "")));
作为参考,link在某些情况下不支持菱形运算符。如果有问题的代码段位于此存储桶中,则可能会进一步挖掘。