使用Java中的Generic方法进行递归调用

时间:2016-01-06 22:40:06

标签: java generics recursion

我正在处理下面的遗留代码

public Map myMethod(Map arg){
      Map newMap = createMap(); //This method creates a new Map instance.
      Iterator entries = arg.entrySet().iterator();
      while (entries.hasNext()) {
          Entry thisEntry = (Entry) entries.next();
          Object key = thisEntry.getKey();
          Object value = thisEntry.getValue();

          if ( value instanceof Map){
               Map newElement = myMethod((Map)value); // Recursive call here
               newMap.put(key, newElement);
          }else if ( value instanceof String ){
               newMap.put(key, value);
          }
      }
      return newMap;
}

显然,我想改编泛型。所以我改变了方法,

public <K,V> Map<K,V> myMethod(Map<K,V> arg){
      Map<K,V> newMap = createMap(); //creates a new Map instance.
      for ( Entry<K,V> entry : arg.entrySet()){

          K key = entry.getKey();
          V value = entry.getValue();

          if ( value instanceof Map){
               V newElement = myMethod(value); // Recursive call here
               newMap.put(key, newElement);
          }else if ( value instanceof String ){
               newMap.put(key, value);
          }
      }
      return newMap;
}

我的问题是关于递归调用的行。到目前为止,我已经尝试了

  1. V newElement = myMethod(value); - &gt;编译错误
  2. Map<K,V> newElement = myMethod(value); - &gt;编译错误
  3. V newElement = (V) myMethod((Map<?,?>)value); - &gt;类型安全警告
  4. ---------编辑----------

    我可以做的进一步假设是

    1. createMap()方法可以更改为createMap(arg)
    2. arg的元素类型不受特定Java类型的限制。这是一个库方法,所以它必须是通用的

2 个答案:

答案 0 :(得分:2)

您的代码逻辑不是类型安全的,因此无法避免类型安全警告。

您使用createMap()创建新地图;该方法盲目地创建一个新地图,没有任何信息,因此它创建的地图类不一定与传入的arg类相同。这意味着您的方法myMethod()返回一个映射不一定是与传入的映射相同的实现类。

在递归调用中,您将我们知道的V实例的地图传递给递归调用,并且您希望返回的内容为V。但这不一定是真的。例如,如果VTreeMap,并且递归调用返回的内容是HashMap,该怎么办?然后它应该无法转换为V,并且你无法从中安全地获得V

答案 1 :(得分:-1)

你的类型都搞砸了。

您的方法需要

Map<K,V> 

并返回

Map<K,V>

这意味着通话

V newElement = myMethod(element); 

必须是

Map<K,V> newElement = myMethod(element);  //element is of type Map<K,V>

和电话

newMap.put(key, newElement);

表示newElement也必须是V。

所以你基本上需要String,Map和V才能拥有一些常见的超类型。在原始代码中,Map确实是

Map<Object, Object>

所以超类型是Object。

我会备份,并问自己为什么你有一张存储地图和字符串的地图。您应该将它们重构为实际代表其类型的对象。

例如,假设它是一个文件系统而Map是一个目录而String是一个文件,那么你应该真正创建一个FileObject,它可以是一个目录或一个文件,你的方法签名就变成了:

Map<K, FileObject> myMethod(Map<K, FileObject> map)