2016-03-12 42 views
3

我正在学习Java,目前正在阅读Joshua Bloch的Effective Java。Map的参数化类型键

在第29项中,他讨论参数化类型键映射到创建类型安全的异构地图。下面是代码:

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。但我不确定恶意客户如何在编译时破坏类型安全。我尝试过各种方法,但我总是遇到编译器错误,正如我所预料的那样。

是否有人可以告诉我,究竟约书亚布洛赫在上面引述的线是什么意思?

回答

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,并且对于该呼叫关闭了类型推断。这就好像调用这个调用的代码是预泛型一样,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 
} 

但只会在运行拿起一个问题,当恶意代码试图做的损害,但它仍然比使用拿起问题时间更好,当一些其他的客户端尝试使用狡猾的实例。

+2

或者,在较少的奇怪的方式:'类integerClass = Integer.class; favorites.putFavorite(integerClass,“foo”);' –

+0

完美。非常感谢。 –

+0

这里的要点是,''在签名有助于避免出现问题,但执行干净的数据,需要防止腐败:putFavorite执行应该做的:'favorites.put(类型,type.cast(实例)) ' –