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

时间:2017-03-05 02:12:46

标签: java generics javac

考虑put接口的java.util.Map方法的规范。

它说:

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

Map是一个通用接口,分别为关键对象和值对象提供两个类型参数KVput方法签名是:

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,它们产生了相同的结果)。

1 个答案:

答案 0 :(得分:1)

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

如果您查看documentation for HashMap.put,您会注意到它根本没有提到ClassCastException。正如评论中所提到的,TreeMap.put会抛出ClassCastException,因为它需要能够准确地比较密钥。

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

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