【问题标题】:Parameterized type keys for MapMap 的参数化类型键
【发布时间】:2016-06-27 22:13:15
【问题描述】:

我正在学习 Java,目前正在阅读 Joshua Bloch 的 Effective Java。

在第 29 条中,他讨论了 Map 的参数化类型键以创建类型安全的异构映射。代码如下:

class Favorites {
    private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();

    public <T> void putFavorite(Class<T> type, T instance) {
        if (type == null)
            throw new NullPointerException("Type is null");
        favorites.put(type, instance);
    }

    public <T> T getFavorite(Class<T> type) {
        return type.cast(favorites.get(type));
    }
}

他接着说

恶意客户端很容易破坏收藏夹的类型安全 例如,只需使用原始形式的 Class 对象。但是 生成的客户端代码在运行时会生成未经检查的警告 编译。

我知道Class&lt;T&gt; 将被删除为 Class。但我不确定恶意客户端如何在编译时破坏类型安全。我尝试了各种方法,但正如我所料,我总是遇到编译器错误。

谁能告诉我上面引用的那句话中 Joshua Bloch 到底是什么意思?

【问题讨论】:

    标签: java generics type-safety raw-types


    【解决方案1】:

    原始类型是没有通用信息的类型。以下是你可以如何打败方法的类型安全:

    Favorites favorites = new Favorites();
    favorites.putFavorite((Class)Integer.class, "foo"); // no compile error
    

    而这不会编译:

    favorites.putFavorite(Integer.class, "foo"); // compile error
    

    因为参数的类型是Class(而不是Class&lt;T&gt;),所以无法确定泛型方法参数T,并且该调用的类型推断已关闭。就好像进行调用的代码是 pre-generics,java 向后兼容(通过忽略泛型)。

    您可以通过以下方式防范该问题:

    public <T> void putFavorite(Class<T> type, T instance) {
        if (type == null)
            throw new NullPointerException("Type is null");
        if (!type.isInstance(instance)) // add check for instance type
            throw new IllegalArgumentException("Class/instance mismatch");
        favorites.put(type, instance);
    }
    

    或更粗暴地(因为您无法通过错误消息提供信息),只需尝试强制转换:

    public <T> void putFavorite(Class<T> type, T instance) {
        if (type == null)
            throw new NullPointerException("Type is null");
        favorites.put(type, type.cast(instance)); // will throw ClassCastException
    }
    

    但这只会在运行时恶意代码试图造成破坏时发现问题,但它仍然比在使用时发现问题要好其他客户端尝试使用狡猾的实例。

    【讨论】:

    • 或者,用一种不那么奇怪的方式:Class integerClass = Integer.class; favorites.putFavorite(integerClass, "foo");
    • 完美。非常感谢。
    • 这里的重点是签名中的&lt;T&gt;有助于避免问题,但是要强制执行干净的数据,您需要防止损坏:putFavorite 实现应该这样做:favorites.put(type, type.cast(instance))
    猜你喜欢
    • 1970-01-01
    • 2021-08-29
    • 2021-10-02
    • 2021-12-06
    • 2020-12-15
    • 1970-01-01
    • 2021-11-28
    • 1970-01-01
    相关资源
    最近更新 更多