我正在学习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>
会被删除为Class。但我不确定恶意客户端如何在编译时破坏类型安全性。我尝试了各种方法,但我总是遇到编译器错误,正如我所料。
有人可以告诉我Joshua Bloch在上面引用的内容中究竟是什么意思吗?
答案 0 :(得分: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
}
但是当恶意代码试图造成损害时,这只会在运行时中发现问题,但它仍然比在使用中解决问题更好>其他一些客户尝试使用狡猾的实例的时间。