Java Generics:通用映射(深层副本)的方法签名

我有一些Map ,它们本身可能再次包含Map (任何类型)。 我用签名写了一个方法:

 public static  HashMap deepCopyHashMap(HashMap s); 

但是,我现在想要概括这个代码以支持Map ,但仍然返回与参数类型相同的对象。 所以代替:

 public static  HashMap deepCopyHashMap(HashMap s); public static  CheckedMap deepCopyCheckedMap(CheckedMap s); public static  TreeMap deepCopyTreeMap(TreeMap s); ... etc. 

我想要这样的东西:

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

但是,这给了我:

 Multiple markers at this line - The type M is not generic; it cannot be parameterized with arguments  - The type M is not generic; it cannot be parameterized with arguments  

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

对于这个项目,添加更多依赖项实际上不是一个选项,所以我更喜欢不依赖于外部库的解决方案。 此外,我已经研究了Cloneable接口,但是它只是一个标记接口(一般没有Map的实现),对我来说没什么用处。


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

 public static  HashMap deepCopyHashMap(HashMap source){ HashMap result = new HashMap(); for(Map.Entry 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的运行时类型的默认构造函数,它将失败。 我已经使用嵌套的HashMap对其进行了测试,并且正确地复制了运行时类型。

     @SuppressWarnings("unchecked") public static <K,V, M extends Map> M deepCopyMap(M source) throws InstantiationException, IllegalAccessException{ M result = (M) source.getClass().newInstance(); for(Map.Entry 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> M deepCopyMap(M source){ M result; if(source instanceof HashMap){ result = (M) new HashMap(); } else { //fail } // etc. add more types here for(Map.Entry 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 > M deepCopyMap(M s); 

您声明的通用定义M已经隐式,因为编译器必须确保M extends Map为真。 因此,定义M是多余的。

至于在方法内部创建副本,它变得更加复杂。 通用类型改进了通用方法的用户的类型安全性。 但是,在方法内部,您就像使用非generics方法一样无能为力,该方法将原始Map作为其参数。 (你当然可以进一步重写generics类型。)

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

 @SupressWarnings("unchecked") public static > 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 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; } 

这对用户来说不是很普遍,如果地图包含一些用户类型地图,很可能会抛出exception。 编译器会在这种方法中警告你几乎任何事情的事实是一个好兆头,这是一个坏主意。

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

旁注:在不限制MV ,定义这些参数没有意义,因为您对这些参数一无所知。 只需使用通配符?

您定义的类型M已经绑定为Map其中> 。 所以只需删除M然后将其M