为什么我会丢失类型信息?

Why am I losing type information?

我发现地图、原始类型和泛型会发生一些有趣的事情。以下代码:

static {
          Map map = new HashMap ();
          Set <Map.Entry> set = map.entrySet ();
          for (Map.Entry entry : set) {} // fine 
          for (Map.Entry entry : map.entrySet()) {} // compilation error
}

我收到有关类型不兼容的编译错误,即:"Object cannot be cast to Entry"。

Ideone for convenience

如果没有变量再次存储它,为什么迭代器 entrySet() 会丢失类型信息?

rawtypes 不应该影响类型,所以 Map.Entry 突然变成了一个对象。还是我记错了?

您的示例使您看起来拥有从未有过的类型信息。您写了:

Map map = new HashMap ();
Set <Map.Entry> set = map.entrySet();
for (Map.Entry entry : set) {} // fine 
for (Map.Entry entry : map.entrySet()) {} // compilation error

但是 map.entrySet() 返回的是 Set,而不是 Set <Map.Entry>。您执行了一项未经检查的作业,其中 "adds" 类型信息。

在第二个 for 循环中,我们不知道 Set 里面有什么,所以我们不能在没有显式转换的情况下迭代 Set <Map.Entry>

例如,将原始示例与我们不使用未经检查的分配 "add" 输入信息的示例进行比较。

Map map = new HashMap();
Set set = map.entrySet();
for (Map.Entry entry : set) {
} // Object cannot be cast to Entry
for (Map.Entry entry : map.entrySet()) {
} // Object cannot be cast to Entry

在这种情况下,两个 for 循环都会产生编译错误。

Java 语言规范第 4.8 节中记录了此行为:

The type of a constructor (§8.8), instance method (§8.8, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the erasure of its type in the generic declaration corresponding to C. The type of a static member of a raw type C is the same as its type in the generic declaration corresponding to C.

我认为简短的回答是 Java 在某些情况下允许 "unchecked cast" 但在其他情况下不允许。处理原始类型(没有指定类型的通用类型)就是其中之一。

请记住,for (Map.Entry entry : set) 等同于:

Iterator it = set.iterator();
while (it.hasNext())
{
    Map.Entry entry = it.next();
}

作业:

Set set = map.entrySet();

是允许的并且不会产生任何警告,因为您没有引入任何新类型,但是在 for 循环中 it.next() 将 return 类型 Object 和如果你在没有显式转换的情况下分配它,你将得到编译器异常。

作业:

Set <Map.Entry> set = map.entrySet(); 
允许

但会生成 "unchecked cast" 警告,因为显式类型 Map.Entry 并且在 for 循环中 it.next() 将 return 类型 Map.Entry 并且作业会正常工作。

您可以像这样将显式强制转换放在 for 循环中:

for(Map.Entry entry : (Set<Map.Entry>) map.entrySet())