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

时间:2015-04-14 16:55:17

标签: java iterator type-erasure generics

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

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
}

我收到有关Type不兼容性的编译错误,即:&#34; Object无法转换为Entry&#34;。

Ideone for convenience

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

rawtypes不应该影响类型,以便Map.Entry突然成为一个Object。或者我错了?

2 个答案:

答案 0 :(得分:7)

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

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>。您已执行未经检查的作业,该作业会“添加”类型信息。

在第二个for循环中,我们不知道Set中的内容,因此如果没有显式强制转换,我们就无法迭代Set <Map.Entry>

例如,将原始示例与我们未使用未选中的作业“添加”类型信息的示例进行比较。

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节

中记录了此行为
  

构造函数的类型(§8.8),实例方法(§8.8,§9.4),或者   非静态字段(第8.3节)未从中继承的原始类型C的M.   它的超类或超级接口是它的类型的擦除   对应于C的泛型声明。静态成员的类型   原始类型C与泛型声明中的类型相同   对应于C.

答案 1 :(得分:4)

我认为简短的回答是Java在某些情况下允许“未经检查的强制转换”,而在其他情况下则不允许。处理原始类型(没有指定类型的泛型类型)是这些实例之一。

请注意,for (Map.Entry entry : set)相当于:

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

作业:

Set set = map.entrySet();

是允许的,不会生成任何警告,因为您没有引入任何新类型,但在for循环it.next()将返回类型Object,如果Set <Map.Entry> set = map.entrySet(); ,您将收到编译器异常你没有明确的演员就分配它。

作业:

Map.Entry

是允许的,但会因显式类型for生成“未经检查的强制转换”警告,并且it.next()循环Map.Entry将返回类型for(Map.Entry entry : (Set<Map.Entry>) map.entrySet()) ,并且分配将工作得很好。

您可以将显式强制转换放在for循环中,如下所示:

{{1}}