为什么这个通用的java代码不能编译?

在这个简化的例子中,我有一个generics的类和一个返回一个Map的方法,而不pipetypes参数。 为什么当我没有在包含类中指定types时,编译器会擦除地图上的types?

import java.util.Map; public class MyClass<T> { public Map<String, String> getMap() { return null; } public void test() { MyClass<Object> success = new MyClass<Object>(); String s = success.getMap().get(""); MyClass unchecked = new MyClass(); Map<String, String> map = unchecked.getMap(); // Unchecked warning, why? String s2 = map.get(""); MyClass fail = new MyClass(); String s3 = fail.getMap().get(""); // Compiler error, why? } } 

我得到这个编译器错误。

 MyClass.java:20: incompatible types found : java.lang.Object required: java.lang.String String s3 = fail.getMap().get(""); // Compiler error 

得到它了。 这实际上不是一个错误,看起来很奇怪。

JLS第4.8节(原始types)

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

所以即使方法的types签名没有使用类本身的任何types参数,input擦除也会启动并且签名变得有效

 public Map getMap() 

换句话说,我认为你可以想象一个原始types与genericstypes是相同的API,但是所有的<X>位在所有地方都被删除(在API中,而不是在实现中)。

编辑:此代码:

 MyClass unchecked = new MyClass(); Map<String, String> map = unchecked.getMap(); // Unchecked warning, why? String s2 = map.get(""); 

编译是因为从原始的Maptypes到Map<String, String>有一个隐式但未经检查的转换。 您可以通过在最后一种情况下进行显式转换(在执行时不做任何事情)来获得相同的效果:

 // Compiles, but with an unchecked warning String x = ((Map<String, String>)fail.getMap()).get(""); 

嗯…不幸的是,我不能告诉你为什么失败。 但我可以给你一个简单的解决方法:

fail的types更改为MyClass<?> ,然后它将编译得很好。

非常有趣的问题,Jon Skeet非常有趣的答案。

我只想添加一些关于java编译器的这种行为的愚蠢或不愚蠢的东西。

我认为,编译器假定如果您不指定generc类中的types参数,则根本无法(或不希望)使用任何types参数 。 您可以使用早于5的Java版本,或者喜欢手动进行强制转换。

这对我来说似乎并不那么愚蠢。

genericstypes在编译后被擦除。

当你这样做时:

 Map<String, String> map = unchecked.getMap(); 

你强制从地图转换为地图<String,String>,这就是为什么未选中的警告。 但是,之后你可以这样做:

 String s2 = map.get(""); 

因为地图的types是Map <String,String>。

但是,当你这样做

 String s3 = fail.getMap().get(""); 

你不是将fail.getMap()投射到任何东西,所以它被认为是明显的Map,而不是Map <String,String>。

后者应该做的是:

 String s3 = ((Map<String, String>fail.getMap()).get(""); 

这仍然会发出警告,但无论如何将工作。