为什么这个通用的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("");
编译是因为从原始的Map
types到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("");
这仍然会发出警告,但无论如何将工作。