I'm trying to understand why this code has an unchecked cast warning. The first two casts have no warning, but the third does:
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
}
This is the full warning for the stringMap3
cast:
Type safety: Unchecked cast from
Map<capture#3-of ?,Integer>
toStringMap<Integer>
However, the StringMap
class declaration specifies the first type parameter of Map
(i.e., String
), and both map3
and the StringMap<Integer>
cast use the same type for the second type parameter of Map
(i.e., Integer
). From what I understand, as long as the cast doesn't throw ClassCastException
(and it shouldn't since there is an instanceof
check), stringMap3
would be a valid Map<String, Integer>
.
Is this a limitation of the Java compiler? Or is there a scenario where calling methods of either map3 or stringMap3 with certain arguments may result in an unexpected ClassCastException
if the warning is ignored?
The behavior is as specified. In Section 5.5.2 of the Java Language Specification an unchecked cast is defined as:
(where
A <: B
means: "A
is a subtype ofB
").In your first example, the target type has no wildcards (and thus all of them are unbounded). In your second example,
StringMap<Integer>
is actually a subtype ofMap<String, Integer>
(and there is no subtypeX
as mentioned in the third condition).In your third example, however, you have a cast from
Map<?, Integer>
toStringMap<Integer>
, and, because of the wildcard?
, neither is a subtype of the other. Also, obviously, not all type parameters are unbounded wildcards, so none of the conditions apply: it is an unchecked exception.If an unchecked cast occurs in the code, a conforming Java compiler is required to issue a warning.
Like you, I do not see any scenario where the cast would be invalid, so you could argue that it is a limitation of the Java compiler, but at least it is a specified limitation.