Java 泛型:泛型映射(深拷贝)的方法签名

2024-03-28

我有几个Map其本身可能再次包含Maps(任何类型)。我写了一个带有签名的方法:

public static <K,V> HashMap<K,V> deepCopyHashMap(HashMap<K,V> s);

但是,我现在想概括此代码以支持Map一般而言,但仍然返回与参数类型相同的对象。所以而不是:

public static <K,V> HashMap<K,V> deepCopyHashMap(HashMap<K,V> s);
public static <K,V> CheckedMap<K,V> deepCopyCheckedMap(CheckedMap<K,V> s);
public static <K,V> TreeMap<K,V> deepCopyTreeMap(TreeMap<K,V> s);
...
etc.

我想要这样的东西:

public static <K,V, M extends Map<K,V>> M<K,V> deepCopyMap(M<K,V> s);

然而,这给了我:

Multiple markers at this line
- The type M is not generic; it cannot be parameterized with arguments <K, 
 V>
- The type M is not generic; it cannot be parameterized with arguments <K, 
 V>

如何正确声明方法签名并仍然返回正确类型的对象(内部不使用反射)?

对于这个项目来说,添加更多依赖项确实不是一个选择,所以我更喜欢一个不依赖外部库的解决方案。另外,我还调查了Cloneable接口,但是它只是一个标记接口(没有实现Map一般而言)这对我来说没有多大用处。


编辑: 作为参考,这是我的深度复制嵌套代码HashMaps(代码工作正常):

public static <K,V> HashMap<K,V> deepCopyHashMap(HashMap<K,V> source){
    HashMap<K,V> result = new HashMap<K, V>();
    for(Map.Entry<K, V> entry : source.entrySet()){
        K k = entry.getKey();
        V v = entry.getValue();
        if(k instanceof HashMap<?,?>){
            k = (K) deepCopyHashMap((HashMap<?,?>) k);
        }
        if(v instanceof HashMap<?,?>){
            v = (V) deepCopyHashMap((HashMap<?,?>) v);
        }
        result.put(k, v);
    }
    return result;
}

编辑:解决方案

  1. 这不是一个理想的解决方案。如果嵌套的运行时类型没有默认构造函数,它将失败Map。我已经用嵌套测试了它HashMaps 并且运行时类型已正确复制。

    @SuppressWarnings("unchecked")
    public static <K,V, M extends Map<K,V>> M deepCopyMap(M source) throws InstantiationException, IllegalAccessException{
        M result = (M) source.getClass().newInstance();
        for(Map.Entry<K, V> entry : source.entrySet()){
            K k = entry.getKey();
            V v = entry.getValue();
            if(k instanceof Map<?,?>){
                k = (K) deepCopyMap((Map<?,?>) k);
            }
            if(v instanceof Map<?,?>){
                v = (V) deepCopyMap((Map<?,?>) v);
            }
            result.put(k, v);
        }
        return result;
    }
    
  2. 这更安全,但需要显式列出所有已知类型:

    @SuppressWarnings("unchecked")
    public static <K,V, M extends Map<K,V>> M deepCopyMap(M source){
        M result;
        if(source instanceof HashMap){
            result = (M) new HashMap<K,V>();
        } else {
            //fail
        }
        // etc. add more types here
        for(Map.Entry<K, V> entry : source.entrySet()){
            K k = entry.getKey();
            V v = entry.getValue();
            if(k instanceof Map<?,?>){
                k = (K) deepCopyMap((Map<?,?>) k);
            }
            if(v instanceof Map<?,?>){
                v = (V) deepCopyMap((Map<?,?>) v);
            }
            result.put(k, v);
        }
        return result;
    }
    

泛型类型参数本身不能是泛型的。只需删除通用定义即可M:

public static <K, V, M extends Map<K, V>> M deepCopyMap(M s);

通用定义M<K, V>你所说的已经是隐式的,因为编译器必须确保M extends Map<K, V>是真的。因此,有一个定义M<K, V>是多余的。

至于在方法内部创建副本则变得更加复杂。泛型类型提高了泛型方法用户的类型安全性。然而,在该方法内部,您就像使用了采用原始方法的非泛型方法一样无能为力。Map作为其论点。 (当然,您可以进一步限制泛型类型。)

毕竟,我不会向您推荐您建议的方法。您建议您的 API 用户可以深度克隆任何类型的Map它作为方法的参数提供。然而,你不能。Map是一个公共接口,任何人都可以实现它。在运行时,您可能会被要求创建一个您未知的深度克隆映射,而您将无法这样做。看看这个实现:

@SupressWarnings("unchecked")
public static <K, V, M extends Map<K, V>> M deepCopyMap(M s) {
    Map map;
    if(s.getClass() == HashMap.class) {
      map = new HashMap();
    } else if(s.getClass == LinkedHashMap.class) {
      map = new LinkedHashMap();
    } else {
      throw new RuntimeException("unknown map type " + s.getClass());
    }
    for(Map.Entry<K, V> entry : source.entrySet()) {
        K k = entry.getKey();
        V v = entry.getValue();
        if(k instanceof Map) {
          map.put(k, deepCopyMap((Map) k));
        } else {
          result.put(k, v);
        }
    }
    return (M) map;
}

这对于用户来说不是很透明,并且如果映射包含某些用户类型映射,很可能会引发异常。事实上,编译器会警告您此方法中的几乎所有内容,这是一个好兆头,表明这是一个坏主意。

相反,我实际上会推荐您使用重载方法,即只为已知类型提供深度克隆。但是,如果您发现无法在运行时创建的嵌套映射,则必须抛出运行时异常。您正在寻找的类型安全很难实现。此外,我会将其作为合同的隐式部分,即您不能使用嵌套映射,其中映射类型不在指定的组内Map实施。

附注:无限制M and V,定义这些参数是没有意义的,因为您对这些参数一无所知。只需使用通配符?.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 泛型:泛型映射(深拷贝)的方法签名 的相关文章

随机推荐

  • Angular http.get() url 作为原始 HTML

    我正在为作业创建一个网站 我想在其中动态加载一些数据 问题是 数据仅来自网站 没有 API 或任何东西 有什么方法可以使用 http get 来将整个网站作为原始 HTML 来提取 然后我可以解析它以获取信息吗 Thanks 您可以设置re
  • 在 AS7 中转换查找的 EJB 视图时出现 ClassCastException

    我正在将 2 个 EAR 部署到 JBoss AS 7 1 0 Alpha1 SNAPSHOT 7 0 1 Final 版本之后 两者都部署得很好 我有一个 EJB Singleton 类打包在一个 JAR 中 位于其中一个 EAR 中 S
  • PHP cURL 上传文件到 Node JS 服务器

    我有一个 PHP 前端项目和 Node JS API 我需要将文件发送到 API 但我必须使用 cURL 因为表单发送到 PHP 而 PHP 通过 cURL 发送到 Node JS 我尝试了很多不同的方法 但我无法让它发挥作用 在我正在使用
  • 未显示视图的屏幕截图

    我试图在显示 MyFirstViewController 时截取 MySecondViewController view 的屏幕截图 我不希望 MySecondViewController 随时出现在屏幕上 那可能吗 这是我当前在 MyFi
  • HashMap 分组依据 (Java)

    有没有一种方法可以在Java中按Key分组并将值添加到HashMap中 HashMap
  • 理解这部分手臂的汇编代码

    syntax unified thumb cpu cortex m4 arch armv7e m fpu fpv4 sp d16 Changes from unprivileged to privileged mode thumb func
  • constexpr 可以和 volatile 结合使用吗?

    以下代码片段在 Clang 3 5 中工作正常 但在 GCC 4 9 2 中不行 int main constexpr volatile int i 5 有错误 错误 此处不能使用 挥发性 和 constexpr 如果我检查 Clang 生
  • 将新的提交添加到现有的 Git 标签

    我创建了一个 Git 标签v1 1 using git tag a v1 1 m my version 1 1 我推了那个标签 后来我做了一些相关的修改v1 1 现在 当我推送新的更改并使用检查 git 标签时git describe它向我
  • GTK 窗口运动动画?

    我想自动在屏幕上移动我的 GTK WINDOW 目前我将它置于绘制 移动循环中 但这非常不稳定 我对 GTK 编程 以及一般的 gui 编程 非常陌生 我缺少什么 您还没有说您希望窗口遵循什么样的路径 如果路径是时间的一些简单函数 也就是说
  • 如何在swift4中创建串行队列[重复]

    这个问题在这里已经有答案了 DispatchQueue init label qos attributes autoreleaseFrequency target 参数怎么写 原著笔记看了半天 还是不会写串行队列 None
  • Bootstrap - 如何用JSP实现模态弹出窗口

    我正在尝试使用 Spring MVC 通过 JSP 实现模式弹出窗口 In my 索引 jsp我有这个href链接 a href findCompany Find company a and 总是在这个 jsp中 有这样的代码片段 div
  • Crypto++“Tee”式过滤器

    我正在研究根据某些条件处理数据流 数据从输入管道读取 处理并推送到 Crypto CBC Mode
  • 如何在Mac中从命令行启动docker

    我在 mac 上安装了 docker 桌面 因此 为了启动 docker 我打开应用程序并找到 docker 然后我可以在顶部栏看到一个泊坞窗图标 稍后我可以从命令行运行 docker 命令 我的问题是如何从命令行启动 docker 本身
  • 使用 Passport.js 登录后如何通过 React 进行“重定向”?

    新手反应问题 我将其与passport js 和express 一起使用 我已成功登录该应用程序 但我不知道如何进行重定向 router post login passport authenticate local function req
  • ASP.NET Mvc - 可为空参数和逗号作为分隔符

    我应该如何在 global asax 中定义路由才能使用可为空的参数和逗号作为分隔符 我正在尝试为我的搜索用户页面实施路由规则 例如 Controller Action name page status Global asax 的完整条目
  • 是否可以将事件传递给 Ajax 表单的 OnBegin 函数?

    我的应用程序中有一个 Ajax 表单 我想将事件传递给OnBegin函数 然后使用event preventdefault 这将阻止表单提交 然后在检查某些条件时我尝试手动提交表单 但它不起作用 我无法弄清楚为什么 Ajax BeginFo
  • npm 从本地位置而不是从网络安装软件包?

    这个问题让我抓狂 npm 数据库中有一个包 但它有一些错误 这些错误已经在 github 中修复 我如何使用修复版本 github 版本 Edit 您可以直接从 GitHub 存储库安装 甚至只使用 GitHub 用户名和存储库名称 npm
  • 从 eclipse 中卸载和删除插件

    我正在从 eclipse 卸载选项中卸载名为 X 的插件 现在为了明确方法 我进入 eclipse 插件目录并从那里删除插件 jar 文件 现在我尝试重新安装相同的插件 并且 eclipse 提示错误 期间发生错误 org eclipse
  • 结构化 Spark 流指标检索

    我有一个具有结构化 Spark 流的应用程序 我想获取一些指标 例如调度延迟 延迟等 通常 此类指标可以在 Spark UI Streaming 选项卡中找到 但是 结构化流不存在此类功能我知道 那么如何获取这些指标值呢 目前 我尝试使用查
  • Java 泛型:泛型映射(深拷贝)的方法签名

    我有几个Map其本身可能再次包含Maps 任何类型 我写了一个带有签名的方法 public static