未经检查的转换为实现 Map<String, V> 的泛型类

我试图理解为什么这个代码有一个未经检查的强制转换警告。前两个演员没有警告,但第三个演员有:

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<capture#3-of ?,Integer>StringMap<Integer>

但是,类声明指定了 第一个类型参数 (即 ),并且两者和强制转换都对第二个类型参数 (即 ) 使用相同的类型。据我所知,只要演员不投掷(并且不应该,因为有检查),将是有效的 。StringMapMapStringmap3StringMap<Integer>MapIntegerClassCastExceptioninstanceofstringMap3Map<String, Integer>

这是Java编译器的局限性吗?或者,如果忽略警告,则使用某些参数调用 map3 或 stringMap3 的方法可能会导致意外情况?ClassCastException


答案 1

行为符合指定。在 Java 语言规范的第 5.5.2 节中,未经检查的强制转换定义为:

从类型到参数化类型的强制转换将处于未选中状态,除非至少满足以下条件之一:ST

  • S <: T

  • 的所有类型参数都是无界通配符T

  • T <: S并且除了 的类型参数不包含在 的类型参数中之外,没有其他子类型。SXTXT

(其中表示:“ 是 的子类型”)。A <: BAB

在第一个示例中,目标类型没有通配符(因此所有通配符都是无限的)。在第二个示例中,实际上是 的子类型(并且没有第三个条件中提到的子类型)。StringMap<Integer>Map<String, Integer>X

但是,在第三个示例中,您有一个从 到 的强制转换,并且由于通配符 ,两者都不是另一个的子类型。此外,显然,并非所有类型参数都是无界通配符,因此没有一个条件适用:这是一个未经检查的异常。Map<?, Integer>StringMap<Integer>?

如果代码中发生未经检查的强制转换,则需要符合要求的 Java 编译器发出警告。

像你一样,我没有看到任何强制转换无效的情况,所以你可以争辩说这是Java编译器的限制,但至少它是一个特定的限制。


答案 2

这个演员阵容并不安全。假设您有:

Map<?, Integer> map3 = new HashMap<String,Integer>();
StringMap<Integer> stringMap3 = (StringMap<Integer>)map3;

这将引发异常。您知道自己新建了一个并将其分配给map3并不重要。您正在执行的操作称为 Java 中的 Downcasting,以获取更多信息。StringMap<Integer>

编辑:你也把所有泛型的问题都复杂化了,在没有任何泛型类型的情况下,你会遇到完全相同的问题。


推荐