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

时间:2022-09-25 12:24:19

I have found something interesting to happen with Maps, rawtypes and generics. Following code:

我发现了地图,原型和泛型有趣的事情。以下代码:

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}

I am getting a compilation error about a Type incompatibility, namely: "Object cannot be cast to Entry".

我收到关于类型不兼容的编译错误,即:“对象无法转换为条目”。

Ideone for convenience

为方便起见

Why is the iterator over entrySet() losing the type information if there's no variable storing it again?

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

The rawtypes shouldn't affect the type so that Map.Entry suddenly is an Object. Or am I mistaken?

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

2 个解决方案

#1


Your example makes it look like you have type information that you never had. You have written:

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

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

But map.entrySet() is returning Set, not Set <Map.Entry>. You've performed an unchecked assignment which "adds" type information.

但是map.entrySet()返回Set,而不是Set 。您已执行未选中的作业,该作业会“添加”类型信息。

In the second for loop, we don't know what's inside the Set, so we can't iterate over Set <Map.Entry> without an explicit cast.

在第二个for循环中,我们不知道Set中的内容,因此我们不能在没有显式强制转换的情况下迭代Set

For example, compare the original example to this one where we don't "add" type information with the unchecked assignment.

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

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

In this case, both for loops produce a compilation error.

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

This behaviour is documented in the Java Language Specification, section 4.8:

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.

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

#2


I think the short answer is that Java allows "unchecked cast" in some situations but not others. Dealing with raw types (Generic types without a specified type) is one of these instances.

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

Keep in mind that for (Map.Entry entry : set) is equivalent to:

请记住,for(Map.Entry entry:set)相当于:

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

The assignment:

Set set = map.entrySet();

is allowed and will not generate any warning, as you are not introducing any new type, but in the for loop it.next() will return type Object and you will get compiler exception if you assign it without an explicit cast.

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

The assignment:

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

is allowed but will generate an "unchecked cast" warning because of the explicit type Map.Entry and in the for loop it.next() will return type Map.Entry and the assignment will work fine.

允许但是会因为显式类型Map.Entry而生成“未经检查的强制转换”警告,并且在for循环中,it.next()将返回类型Map.Entry并且赋值将正常工作。

You can put the explicit cast in the for loop like this:

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

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

#1


Your example makes it look like you have type information that you never had. You have written:

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

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

But map.entrySet() is returning Set, not Set <Map.Entry>. You've performed an unchecked assignment which "adds" type information.

但是map.entrySet()返回Set,而不是Set 。您已执行未选中的作业,该作业会“添加”类型信息。

In the second for loop, we don't know what's inside the Set, so we can't iterate over Set <Map.Entry> without an explicit cast.

在第二个for循环中,我们不知道Set中的内容,因此我们不能在没有显式强制转换的情况下迭代Set

For example, compare the original example to this one where we don't "add" type information with the unchecked assignment.

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

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

In this case, both for loops produce a compilation error.

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

This behaviour is documented in the Java Language Specification, section 4.8:

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.

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

#2


I think the short answer is that Java allows "unchecked cast" in some situations but not others. Dealing with raw types (Generic types without a specified type) is one of these instances.

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

Keep in mind that for (Map.Entry entry : set) is equivalent to:

请记住,for(Map.Entry entry:set)相当于:

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

The assignment:

Set set = map.entrySet();

is allowed and will not generate any warning, as you are not introducing any new type, but in the for loop it.next() will return type Object and you will get compiler exception if you assign it without an explicit cast.

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

The assignment:

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

is allowed but will generate an "unchecked cast" warning because of the explicit type Map.Entry and in the for loop it.next() will return type Map.Entry and the assignment will work fine.

允许但是会因为显式类型Map.Entry而生成“未经检查的强制转换”警告,并且在for循环中,it.next()将返回类型Map.Entry并且赋值将正常工作。

You can put the explicit cast in the for loop like this:

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

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