Java - 扩展HashMap - 对象与泛型行为

时间:2012-04-27 16:08:41

标签: java generics inheritance casting

我正在编写一个基于HashMap的简单缓存,其工作原理如下:

  1. 如果请求的key 在缓存中,请返回其value
  2. 如果请求的key 不是,请运行根据value生成key的方法,同时存储两者,返回value
  3. 代码:

    import java.util.HashMap;
    
    abstract class Cache<K, V> extends HashMap<K, V> {  
        @Override
        public V get(Object key) {
            if (containsKey(key)) {
                return super.get(key);
            } else {
                V val = getData(key);
                put((K)key, val);    // this is the line I'm discussing below
                return val;
            }
        }
    
        public abstract V getData(Object key);
    }
    

    这很简单,效果很好。但是,我讨厌 Sun决定get()Object作为其参数,而不是K。我已经阅读了足够的知识,知道它背后有一些理由(我不同意,但这是另一个故事)。

    我的问题出现在注释行中,因为似乎未选中强制转换 。由于类型擦除,我无法检查key是否为K类型(正确的put()功能所需),因此该方法容易出错。

    一种解决方案是从“is a”切换到“has a”HashMap关系,这种关系更好更干净,但是Cache无法实现Map好几个原因。代码:

    import java.util.HashMap;
    import java.util.Map;
    
    abstract class Cache<K, V> {
        private final Map<K, V> map = new HashMap<K, V>();
    
        public V get(K key) {
            if (map.containsKey(key)) {
                return map.get(key);
            } else {
                V val = getData(key);
                map.put(key, val);
                return val;
            }
        }
    
        public abstract V getData(K key);
    }
    

    任何人都可以提出任何其他(甚至是hackish)解决方案,这样我就可以将Cache维持为Map,并且在get(Object key)和{put(K key, V val)方面仍然是类型安全的{1}}?

    我唯一能想到的是制作另一个名为getValue(Key k)的方法,该方法将委托给get(Object key),但是我不能强迫任何人使用新方法而不是通常的方法

2 个答案:

答案 0 :(得分:15)

不。您已经找到了切换到“has-a”关系的正确解决方案。 (坦率地说,让get方法计算一个新值(如果一个尚不存在)是令人惊讶的,违反了Map契约,并且可能导致许多其他方法的极其奇怪的行为。很大一部分原因是为什么Guava离开MapMaker,这几乎提供了这种确切的行为 - 因为它只是riddled有问题。)

那说,例如番石榴的Cache确实暴露了Map<K, V> asMap() 视图,这是你可以做的事情。这样可以在不影响类型安全的情况下为Map提供大部分优势。

答案 1 :(得分:0)

明确地说,has-a关系是正确的实现。应该从缓存类中删除生成值的业务逻辑。