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

Java 为什么我会丢失类型信息?,java,iterator,type-erasure,generics,Java,Iterator,Type Erasure,Generics,我发现一些有趣的事情发生在映射、rawtype和泛型上。以下代码: static { Map map = new HashMap (); Set <Map.Entry> set = map.entrySet (); for (Map.Entry entry : set) {} // fine for (Map.Entry entry : map.entrySet()) {} // compilation

我发现一些有趣的事情发生在映射、rawtype和泛型上。以下代码:

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
}
静态{
Map Map=newhashmap();
Set=map.entrySet();
对于(Map.Entry:set){}//fine
对于(Map.Entry:Map.entrySet()){}//编译错误
}
我得到一个关于类型不兼容的编译错误,即:“对象不能转换为条目”

如果没有变量再次存储类型信息,为什么
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
在这种情况下,两个for循环都会产生编译错误

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

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


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

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

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

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

任务:

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

Set=map.entrySet()是允许的“未经检查的分配”。这就是允许第一个迭代器工作的原因。在第二种情况下,通过显式转换可以获得相同的结果:
for(Map.Entry:(Set)Map.entrySet()){}
要遵循的最基本规则是:永远不要使用原始类型。它们很容易避免(通常替换为通配符参数,即
Set
变成
Set
),它们存在的唯一原因是与前泛型代码的向后兼容性。@JoachimSauer同意,但我认为这主要是一个学术问题。编辑:好吧,这不是学术问题,但无论如何这是一个有趣的问题。@Joachim是对的。这在维护前泛型时代的遗留代码时没有帮助,而且对这个问题也没有任何意义……”但map.entrySet()返回的是Set,而不是Set。”->这似乎与文档相矛盾,文档明确指出
entrySet()的返回类型
is
Set
@Unihedro:区别在于
map
有一个原始类型,这意味着在调用方法时,所有泛型信息都将被忽略。因此
map.entrySet()
确实返回了
Set
而不是
Set
@Unihedro,如果您在声明中声明了泛型类型,则只能引用
map.Entry
。通过使用rawtypes,您已经更改了方法契约。泛型类的Javadoc没有提到,如果没有给出类型信息,它们将返回rawtype(例如,但这就是它们的设计方式)。这也在“如果方法或构造函数的签名被擦除,则方法的返回类型和泛型方法或构造函数的类型参数也将被擦除”中提到。