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

Java-扩展HashMap-对象与泛型行为,java,generics,inheritance,casting,Java,Generics,Inheritance,Casting,我正在编写一个简单的基于HashMap的缓存,其工作原理如下: 如果请求的键在缓存中,则返回其值 如果请求的键不在那里,请运行基于键生成值的方法,存储两者,返回值 守则: import java.util.HashMap; abstract class Cache<K, V> extends HashMap<K, V> { @Override public V get(Object key) { if (containsKey(key

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

  • 如果请求的
    在缓存中,则返回其
  • 如果请求的
    不在那里,请运行基于
    生成
    的方法,存储两者,返回
  • 守则:

    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);
    }
    
    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);
    }
    
    有人能想出任何其他(甚至是黑客的)解决方案,这样我就可以保持
    缓存
    成为
    映射
    ,并且在
    get(Object key)
    put(K key,V val)
    方面仍然是类型安全的吗


    我唯一能想到的就是创建另一个名为
    getValue(Key k)
    的方法,它将委托给
    get(Object Key)
    ,但是我不能强迫任何人使用新方法来代替通常的方法。

    不。你已经找到了一个正确的解决方案,那就是转换成“有-有”的关系。(坦率地说,让
    get
    方法在不存在的情况下计算一个新值是令人惊讶的,它违反了
    Map
    合同,并可能导致许多其他方法出现极其奇怪的行为。这就是为什么Guava离开了
    MapMaker
    ,后者提供了几乎完全相同的行为——因为问题也是如此。)


    也就是说,例如Guava的
    缓存
    会公开一个
    映射asMap()
    视图,这是您可以做的事情。这在不影响类型安全的情况下为您提供了
    映射的大部分优势。

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

    您可以实现
    getA
    myget
    您不能更改Map.get()的行为,因为您不能强制重新编译使用接口而不是直接使用类的代码。这就是它的有趣之处-没有逻辑!该方法是
    抽象的
    ,因此只能在缓存实例化时指定(实现、重写-以您最喜欢的为准)。我甚至试图使类
    最终抽象
    (尽管我100%知道它不起作用)或只是
    最终
    (方法可由匿名内部类重写-当然也不起作用)为了确保没有人会试图用一些业务逻辑对这段危险的代码进行子类化。似乎
    映射器确实做了我所做的:)。我部分意识到这种奇怪的行为。方法
    V getData(K键)
    abstract
    ,因此仅在缓存实例化时指定(实现)。我甚至试图使类
    最终抽象
    (尽管我100%知道它不起作用)或只是
    最终
    (方法可由匿名内部类重写-当然也不起作用),以确保没有人会试图用一些业务逻辑对这段危险的代码进行子类化。无论如何,非常感谢,与
    asMap()
    的“has-a”关系给出了它的视图。顺便说一句,我不敢相信我错过了番石榴的缓存。出于自豪,我现在不会使用它(当我有自己的实现时),但如果我昨天知道的话…:)无论如何,你应该考虑使用番石榴的<代码> Cache < /代码>。它在谷歌的产品中被使用,所以它经过了大量的测试和优化,并且有很多方便的功能。切换到“HAS-A”关系(一般用于其他Java程序)真的可以吗?@KeenanGebze HAS-A(composition)是最受欢迎的,是的。无论如何,Is-a(继承)通常被错误地使用(就像这里的例子),所以它应该只在真正需要的时候使用,并且当您确信它在架构上是合理的时候使用。