我试图理解为什么这段代码有一个未经检查的强制转换警告。前两个强制转换没有警告,但第三个有:
class StringMap<V> extends HashMap<String, V> {
}
class StringToIntegerMap extends HashMap<String, Integer> {
}
Map<?, ?> map1 = new StringToIntegerMap();
if (map1 instanceof StringToIntegerMap) {
StringToIntegerMap stringMap1 = (StringToIntegerMap)map1; //no unchecked cast warning
}
Map<String, Integer> map2 = new StringMap<>();
if (map2 instanceof StringMap) {
StringMap<Integer> stringMap2 = (StringMap<Integer>)map2; //no unchecked cast warning
}
Map<?, Integer> map3 = new StringMap<>();
if (map3 instanceof StringMap) {
StringMap<Integer> stringMap3 = (StringMap<Integer>)map3; //unchecked cast warning
}
这是stringMap3
强制转换的完整警告:
类型安全:Map中未经检查的强制转换
但是,StringMap
类声明指定了Map
的第一个类型参数(即String
),以及map3
和StringMap
这是Java编译器的限制吗?或者如果忽略警告,使用某些参数调用map3或stringMap3的方法可能会导致意外的ClassCastException
?
行为符合规定。在Java语言规范的第5.5.2节中,未经检查的强制转换定义为:
从类型S
到参数化类型T
的强制转换未选中,除非以下至少一项为真:
>
S
T
的所有类型参数都是无界通配符
T
(其中A
在您的第一个示例中,目标类型没有通配符(因此它们都是无界的)。在您的第二个示例中,StringMap
但是,在您的第三个示例中,您有来自Map的强制转换
如果代码中发生未经检查的强制转换,则需要符合要求的Java编译器发出警告。
像你一样,我没有看到强制转换无效的任何场景,所以你可以争辩说这是Java编译器的限制,但至少它是一个指定的限制。
这个石膏不安全。假设你有:
Map<?, Integer> map3 = new HashMap<String,Integer>();
StringMap<Integer> stringMap3 = (StringMap<Integer>)map3;
这将抛出一个异常。你知道你更新了一个StringMap并不重要
编辑:你也把所有泛型的问题复杂化了,没有任何泛型类型,你会遇到完全相同的问题。
实际上答案在Java语言规范中。第5.1.10节提到,如果您使用通配符,那么它将是一个新的捕获类型。
这意味着,Map
从编译器的角度来看,赋值和强制转换之间可能有任何东西,所以即使map3是StringMap的实例
回答您的问题:是的,直到map3实例只有字符串作为键,而您的代码不可能保证这一点。
看看为什么不:
Map<?, Integer> map3 = new StringMap<Integer>();
// valid operation to use null as a key. Possible NPE depending on the map implementation
// you choose as superclass of the StringMap<V>.
// if you do not cast map3, you can use only null as the key.
map3.put(null, 2);
// but after an other unchecked cast if I do not want to create an other storage, I can use
// any key type in the same map like it would be a raw type.
((Map<Double, Integer>) map3).put(Double.valueOf(0.3), 2);
// map3 is still an instance of StringMap
if (map3 instanceof StringMap) {
StringMap<Integer> stringMap3 = (StringMap<Integer>) map3; // unchecked cast warning
stringMap3.put("foo", 0);
for (String s : stringMap3.keySet()){
System.out.println(s+":"+map3.get(s));
}
}
结果:
null:2
foo:0
Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String