4

我正在学习 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<T>会被删除到班级。但我不确定恶意客户端如何在编译时破坏类型安全。我尝试了各种方法,但正如我所料,我总是遇到编译器错误。

有人可以告诉我上面引用的那句话中约书亚布洛赫到底是什么意思吗?

4

1 回答 1

10

原始类型是没有通用信息的类型。以下是您如何克服方法的类型安全性:

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

而这不会编译:

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

因为参数的类型是Class(而不是),所以无法确定Class<T>泛型方法参数,并且为该调用关闭了类型推断。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
}

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

于 2016-03-12T17:38:12.490 回答