为什么 Map#put 指定@throws ClassCastException?

Why does Map#put specify @throws ClassCastException?

考虑 put method of the java.util.Map 接口的规范。

它说:

@throws ClassCastException if the class of the specified key or value 
        prevents it from being stored in this map

Map 是具有两个 类型参数 的通用接口:KV 分别用于键对象和值对象。 put 方法签名是:

V put(K key, V value);

在什么情况下调用此方法可能会在运行时导致 ClassCastException

我认为如果存在一些泛型和原始类型的混合,那么在忽略编译器警告的同时,这可能会发生。因此,在重读 Effective Java 2 之后,我根据 unsafeAdd 示例(项目 #23,第 112 页)建模了以下内容:

public class MapClassCastException {
    public static void main(String[] args) {
        Map<Integer, String> m = new HashMap<>();
        unsafePut(m);
        System.out.println(m.get(1));
    }

    private static void unsafePut(Map m) { // beware: Raw Type
        m.put("blow", "up");
        m.put(new int[]{2, 4}, "xxx");
    }
}

但是这段代码 而不是 在运行时以 ClassCastException 失败,而是打印 null。 (当然,我忽略编译器警告只是为了更好地理解它)。

有人可以演示 Map#put 如何在运行时抛出 ClassCastException 吗? (我用 JDK 1.6、1.7 和 1.8 尝试过,它们产生了相同的结果)。

Map 是一个接口,它本质上为该接口的所有实现定义了一个契约。 Map.put 的文档告诉您此方法可能会抛出 ClassCastException。这并不 要求 所有实现都抛出它。

如果您查看 documentation for HashMap.put, you'll notice that it doesn't mention ClassCastException at all. As mentioned in the comments, TreeMap.put 确实会抛出一个 ClassCastException 因为它需要能够准确地比较键。

如果您想实现自己的 java.util.Map,如果您想限制键或值的类型,则可以抛出 ClassCastException,即使接口上的泛型无法强制执行如果您碰巧使用原始 Map 类型。

我认为这只是 Collections API 的实现者所说的要小心。