在这个简化的例子中,我有一个泛型类,以及一个不管类型参数如何都返回Map的方法。当我没有在包含类上指定类型时,为什么编译器会清除地图上的类型?
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
答案 0 :(得分:35)
知道了。这实际上不是一个bug,看起来很奇怪。
来自section 4.8 (raw types) of the JLS:
构造函数的类型(§8.8), 实例方法(§8.8,§9.4),或 非静态场(§8.3)原始的M 类型C不是从它继承的 超类或超级接口是 擦除它在通用中的类型 宣言对应C. 原始类型的静态成员的类型 C与其中的类型相同 对应的通用声明 下进行。
因此即使方法的类型签名不使用类本身的任何类型参数,类型擦除也会启动并且签名变得有效
public Map getMap()
换句话说,我认为您可以将原始类型想象为与通用类型相同的API,但是从所有地方删除所有<X>
个(在API中,而不是实现)。
编辑:此代码:
MyClass unchecked = new MyClass();
Map<String, String> map = unchecked.getMap(); // Unchecked warning, why?
String s2 = map.get("");
编译因为从原始Map
类型到Map<String, String>
的隐式但未经检查的转换。通过在最后一种情况下进行显式转换(在执行时无效),您可以获得相同的效果:
// Compiles, but with an unchecked warning
String x = ((Map<String, String>)fail.getMap()).get("");
答案 1 :(得分:3)
将fail
的类型更改为MyClass<?>
,然后编译就可以了。
答案 2 :(得分:3)
非常有趣的问题,以及Jon Skeet的非常有趣的回答。
我只想添加一些关于java编译器的这种行为的愚蠢或不愚蠢的东西。
我认为编译器假定如果你没有在generc类中指定type参数,那么你根本不能(或根本不想)使用任何类型参数。您可以使用早于5的java版本,或者喜欢手动制作演员表。
对我来说这似乎并不那么愚蠢。
答案 3 :(得分:1)
编译后会删除通用类型。
当你这样做时:
Map<String, String> map = unchecked.getMap();
你正在强制从Map转换为Map&lt; String,String&gt;,这就是未选中警告的原因。但是,之后你可以这样做:
String s2 = map.get("");
因为map的类型为Map&lt; String,String&gt;。
然而,当你这样做时
String s3 = fail.getMap().get("");
你没有将fail.getMap()转换为任何东西,所以它被认为是明确的Map,而不是Map&lt; String,String&gt ;.
你应该在后者中做些什么:
String s3 = ((Map<String, String>fail.getMap()).get("");
仍然会发出警告,但无论如何都会有效。