我有一些Map
s本身可能再次包含Map
s(任何类型)。我用签名写了一个方法:
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
的实现),对我来说没什么用。
编辑:
作为参考,这是我深层复制嵌套HashMap
的代码(代码正常工作):
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;
}
这不是一个理想的解决方案..如果嵌套Map
的运行时类型没有默认构造函数,它将失败。我已经使用嵌套的HashMap
测试了它,并且正确地复制了运行时类型。
@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;
}
这样更安全,但需要明确列出所有已知类型:
@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;
}
答案 0 :(得分:9)
通用类型参数不能是自己的通用。只需删除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
和V
的情况下,定义这些参数没有意义,因为您对这些参数一无所知。只需使用通配符?
。
答案 1 :(得分:0)
您定义的类型M
已绑定为Map<K,V>
<K, V, M extends Map<K, V>>
。因此,只需删除M<K,V>
,然后将其设为M
即可。