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

Posted

技术标签:

【中文标题】Java - 扩展 HashMap - 对象与泛型行为【英文标题】:Java - Extending HashMap - Object vs. generics behaviour 【发布时间】:2012-05-08 09:45:33 【问题描述】:

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

    如果请求的key缓存中,则返回其value。 如果请求的key不存在,则运行基于key 生成value 的方法,存储两者,返回value

代码:

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() 功能所必需的),因此该方法容易出错。

一种解决方案是从“是一个”切换到“有一个”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);

任何人都可以提出任何其他(甚至是骇人听闻的)解决方案,以便我可以将Cache 保持为Map 并且仍然可以在get(Object key)put(K key, V val) 的条款中输入安全?

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

【问题讨论】:

你可以实现 getAmyget 你不能改变 Map.get() 的行为,因为你不能强制使用接口而不是你的类的代码直接重新编译。 【参考方案1】:

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

也就是说,例如Guava 的Cache 确实暴露了Map&lt;K, V&gt; asMap() view,这是您可以做的事情。这为您提供了 Map 的大部分优势,而不会影响类型安全。

【讨论】:

看来MapMaker 确实做到了我所做的事情:)。我部分意识到奇怪的行为。方法V getData(K key)abstract,因此只能在缓存实例化时指定(实现)。我什至尝试创建 final abstract 类(尽管我 100% 知道它不会工作)或只是 final (使用匿名内部类可覆盖​​的方法 - 当然也不起作用)确保没有人会尝试使用某些业务逻辑对这段危险的代码进行子类化。无论如何,非常感谢,与asMap() 的“有”关系提供了一个视图 顺便说一句,我不敢相信我错过了番石榴的缓存。出于自豪,我现在不会使用它(当我有自己的实现时),但如果我昨天知道...... :) 你……应该考虑使用 Guava 的Cache。它在 Google 生产中使用,因此经过大量测试和优化,并且具有 lots 的方便功能。 切换到“HAS-A”关系真的可以吗(一般对于其他Java程序)? @KeenanGebze Has-a(作文)是首选,是的。 Is-a(继承)通常被错误地使用,无论如何(就像这里的情况一样),所以它应该只在真正需要并且你确定它在架构上是合理的时候才使用。【参考方案2】:

毫无疑问,has-a 关系是一个正确的实现。应该从缓存类中删除如何生成值的业务逻辑。

【讨论】:

这就是它的有趣之处——没有逻辑!该方法是abstract,因此只能在缓存实例化时指定(实现、覆盖——无论你最喜欢哪一个)。我什至尝试创建 final abstract 类(尽管我 100% 知道它不会工作)或只是 final (使用匿名内部类可覆盖​​的方法 - 当然,也不起作用)确保没有人会尝试使用某些业务逻辑对这段危险的代码进行子类化。

以上是关于Java - 扩展 HashMap - 对象与泛型行为的主要内容,如果未能解决你的问题,请参考以下文章

Java基础之集合与泛型

与泛型、接口和扩展混淆

java基础十二[集合与泛型](阅读Head First Java记录)

TypeScript,啥是对象文字的调用签名以及它们如何与泛型类型一起使用?

Java 泛型接口与泛型方法,以及何时使用其中一种

Java Streams 与泛型的使用