为什么在Java 7中,钻石操作符不能用于java.util.Collections方法?

11
在 Java 1.7.0_55 中,如果我编写此字段声明,则会出现编译错误("类型不兼容")。
   private final Map<String,Object> myMap =
       Collections.synchronizedMap(new HashMap<>());
如果我将其更改为以下内容:
   private final Map<String,Object> myMap =
       Collections.synchronizedMap(new HashMap<String,Object>());

它正常编译。(这里以synchronizedMap作为例子,但对其他Collections方法如unmodifiable*、synchronized*等也适用)

但是我为什么在这里使用钻石操作符不像我预期的那样起作用呢? 因为Collections.synchronizedMap()被声明为:

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {

在我看来,构造函数调用的类型参数必须与字段声明的类型参数相同,并且编译器应该能够基于此推断出构造类的类型参数。

我尝试查找JLS中是否有规定此语法是不可接受的条款,但我找不到。有人能指引我吗?


2
你只是Java 7的类型推断能力差的受害者。 - Boris the Spider
3个回答

12

Java 7编译器不能正确推断类型,导致出现编译错误;而Java 8改进了类型推断机制,成功通过编译。简单来说,Java 7的类型推断无法准确推断类型,但是Java 8的更好的类型推断可以准确地推断类型。

这个变化是 Java 8 的 JEP (JDK Enhancement Proposal) 101

概要

平稳地扩展方法类型推断的范围,支持(i)方法上下文中的推断和(ii)链式调用中的推断。

Java 8能够推断带参数的多个方法调用和方法调用链接中的类型。现在可以从赋值语句的左侧<String, Object> 推断出 Collections.synchronizedMap 的调用到该调用参数中的钻石操作符 new HashMap<>()的类型。


3

当一个方法被声明成这样时

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {

参数提供了流派。

假设我这样做:

public <V> V returnV(V v);

然后我称呼它为:

returnV("myString")

V由myString决定。

当您提供时

new HashMap<>()

那么没有泛型,编译器会猜测你的意思是:

new HashMap<Object, Object>()

所以你会得到:
private final Map<String,Object> myMap =
   Collections.synchronizedMap(new HashMap<Object,Object>());

这是一种不兼容类型的错误。


0

这是因为您试图将new HashMap<>()传递给Collections类的方法。这与执行以下操作不同:

Map <String, Integer> map = new HashMap<>();

您正在使用的方法期望已知类型的 Map。钻石语法只是声明和初始化泛型类的标准语法的简写。


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