Java Generics:泛型地图(深层副本)的方法签名 [英] Java Generics: method signature for (deep copy of) generic Maps

查看:137
本文介绍了Java Generics:泛型地图(深层副本)的方法签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有几个 Map ,它们自己又可能包含 Map (任何类型)。我写了一个签名方法:

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

不过,我现在想概括一下这段代码来支持 Map <一般而言code> s,但仍返回与参数类型相同的对象。因此而不是:

  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);
...

我想这样:

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

然而,这给了我:

 此行的多个标记
- 类型M不是通用的;它不能用参数< K,
V>来参数化。
- 类型M不是通用的;它不能用参数< K,
V>来参数化。

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



对于这个项目来说,添加更多的依赖关系并不是一种选择,所以我更喜欢不依赖于外部库的解决方案。另外,我还查看了 Cloneable 接口,但它只是一个标记接口(没有 Map c>一般来说)它对我没什么用处。






编辑:
作为参考,这是我的深拷贝代码嵌套 HashMap s(代码正常工作):

  public static< K,V>的HashMap< K,V> deepCopyHashMap(HashMap< K,V> source){
HashMap< K,V> result = new HashMap< K,V>(); (Map.Entry< K,V> entry:source.entrySet()){
K k = entry.getKey();
V v = entry.getValue(); (HashMap的实例<?,?>){
k =(K)deepCopyHashMap((HashMap <?,?>)k); (HashMap的实例<?,?>){
v =(V)deepCopyHashMap((HashMap<?,?>)v);
}
result.put(k,v);
}
返回结果;





编辑:解决方案 h2>


  1. 这不是一个理想的解决方案。如果嵌套<$的运行时类型没有默认构造函数C $ C>地图。我已经使用嵌套的 HashMap s对其进行了测试,并且正确地复制了运行时类型。

      @SuppressWarnings(unchecked)
    public static< K,V,M extends Map< K,V>> M deepCopyMap(M source)抛出InstantiationException,IllegalAccessException {
    M result =(M)source.getClass()。newInstance(); (Map.Entry< K,V> entry:source.entrySet()){
    K k = entry.getKey();
    V v = entry.getValue(); (Map <<α,>)k)的情况下的
    。 (v的实例),其中v(v)是v(v)的深度复制图((Map <?,?> v))。
    }
    result.put(k,v);
    }
    返回结果;
    }


  2. 这样安全得多,但所有已知类型都需要明确列出:

      @SuppressWarnings(unchecked)
    public static< K,V,M extends Map< K,V>> M deepCopyMap(M source){
    M结果;
    if(source of instance of HashMap){
    result =(M)new HashMap< K,V>();
    } else {
    //失败
    }
    //等等在这里添加更多类型
    (Map.Entry< K,V>条目:source.entrySet ()){
    K k = entry.getKey();
    V v = entry.getValue(); (Map <<α,>)k)的情况下的
    。 (v的实例),其中v(v)是v(v)的深度复制图((Map <?,?> v))。
    }
    result.put(k,v);
    }
    返回结果;



解决方案

泛型类型参数不能是自己的泛型。只需删除 M 的通用定义:

  public static   

通用定义 M 你说的已经是隐含的了,因为编译器必须确保 M扩展Map< K,V> 为真。因此,定义 M 是多余的。

至于在方法内创建副本它变得更加复杂。通用类型为通用方法的用户提高了类型安全性。但是,在这个方法中,你就像无法使用一个以原始 Map 作为参数的非泛型方法一样无能为力。 (当然,你可以进一步确定泛型类型)。

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

  @SupressWarnings(unchecked)
public static< K,V,M extends地图< 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()); (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;
}

这对用户来说不是非常透明,并且很可能会抛出异常if该地图包含一些用户类型地图。事实上编译器会警告你几乎所有的东西都是这个方法的一个好兆头,这是一个好主意。



实际上,我会推荐你​​重载您只能为已知类型提供深层克隆。但是,如果您发现在运行时无法创建的嵌套地图,则必须抛出运行时异常。您正在寻找的那种类型的安全性很难实现。另外,如果地图类型不在指定的 Map 实现组中,则不能使用嵌套地图。 b
$ b

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


I have a few Maps that themselves again may contain Maps (of any type). I wrote a method with the signature:

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

However, I would now like to generalize this code to support Maps in general, but still return an object of the same type as the argument. So instead of:

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.

I would like something like this:

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

However, this gives me:

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>

How do I properly declare the method signature and still return an object of the correct type (without using reflection internally)?

For this project adding more dependencies is really not an option, so I would prefer a solution that does not rely on external libraries. Also, I have looked into the Cloneable interface, however with it being only a marker interface (with no implementation for Maps in general) it is not of much use to me.


Edit: For reference, this is my code for deep-copying nested HashMaps (code works properly):

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;
}


Edit: Solutions

  1. This is not an ideal solution.. It will fail if the there is no default constructor for the runtime type of the nested Map. I have tested it with nested HashMaps and the runtime type is correctly copied.

    @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. This is much safer, but all the known types need to be listed explicitly:

    @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;
    }
    

解决方案

Generic type parameters cannot be themself generic. Just drop the generic definition for M:

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

The generic definition M<K, V> you stated is already implicit since the compiler must ensure that M extends Map<K, V> is true. Therefore, a definition M<K, V> is redundant.

As for creating a copy inside of the method it gets more complicated. Generic types improve type safety for the users of a generic method. However, inside the method you are just as clueless as if you used a non-generic method which took a raw Map as its argument. (You could of course further restict the generic types.)

After all, I would not recommend you the approach you suggest. You are suggesting the user of your API that you could deeply clone any type of Map that is provided as the method's argument. However, you cannot. Map is a public interface and anybody can implement it. At run time you might be ask to create a deep clone map that is unknown to you and you will not be able to. Look at this implementation:

@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;
}

This is not very transperent to the user and will most likely throw an exception if the map contains some user type map. The fact that the compiler will warn you about almost anything in this method is a good sign for that this is a bad idea.

Instead, I would actually recommend you the overloading approach where you only offer deep clones for known types. If you however discover a nested map that you cannot create at run time, you have to throw a run time exception. The kind of type safety you are looking for is hard to achieve. Additionally, I would make it an implicit part of the contract that you cannot use nested maps where the map types are not within a specified group of Map implementations.

On a side note: Without restricting M and V, there is no point in defining these parameters since you do not know anything about these parameters. Simply use a wildcard ?.

这篇关于Java Generics:泛型地图(深层副本)的方法签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆