简体   繁体   English

Java中的泛型方法的递归调用

[英]Recursive call with Generic method in Java

I am working on legacy code like below 我正在处理如下的旧代码

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

Obviously, I would like to adapt Generics. 显然,我想改编泛型。 So I changed the method like, 所以我改变了方法,

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

My question is about the line of recursive calls. 我的问题是关于递归调用的行。 So far, I have tried 到目前为止,我已经尝试过

  1. V newElement = myMethod(value); -> Compile Error ->编译错误
  2. Map<K,V> newElement = myMethod(value); ->compile Error ->编译错误
  3. V newElement = (V) myMethod((Map<?,?>)value); ->Type safety warning ->输入安全警告

--------- Edited ---------- ---------编辑----------

The further assumption that I could make is 我可以做的进一步假设是

  1. createMap() method could be changed to createMap(arg) createMap()方法可以更改为createMap(arg)
  2. Element type of arg is not bounded to a specific Java type. arg的元素类型不限于特定的Java类型。 This is a library method so it has to be generic 这是一种库方法,因此必须是通用的

The logic of your code is not type-safe, so it's not possible to avoid a type-safety warning. 代码的逻辑不是类型安全的,因此无法避免类型安全警告。

You create a new map using createMap() ; 您可以使用createMap()创建一个新地图; that method is creating a new map blindly, with no information, so the class of map it creates is not necessarily the same as the class of arg that is passed in. That means your method myMethod() returns a map that is not necessarily of the same implementing class as the one that is passed in. 该方法正在盲目创建新地图,没有任何信息,因此它创建的地图类不必与传入的arg类相同。这意味着您的方法myMethod()返回的地图不一定是与传入的类相同的实现类。

In your recursive call, you pass a map that we know is an instance of V into the recursive call, and you want the thing you get back out to be a V . 在递归调用中,您将一个我们知道是V的实例的映射传递到递归调用中,并且您希望返回的对象是V But that is not necessarily true. 但这不一定是正确的。 For example, what if V were TreeMap , and the thing that is returned from the recursive call is a HashMap ? 例如,如果VTreeMap ,而递归调用返回的是HashMap呢? Then it should not be able to cast to V , and there is no way you can safely get a V from that. 这样它就不能转换为V ,并且您不可能从中安全地获得V

Your types are all messed up here. 您的类型都在这里弄乱了。

Your method takes a 您的方法需要

Map<K,V> 

and returns a 并返回一个

Map<K,V>

That means the call 那意味着电话

V newElement = myMethod(element); 

must be 一定是

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

and the call 和电话

newMap.put(key, newElement);

means that newElement must also be of type V. 表示newElement也必须为V类型。

So you basically need String, Map, and V to all have some common super type. 因此,您基本上都需要String,Map和V来具有某种通用的超级类型。 In the original code, Map is really 在原始代码中,Map实际上是

Map<Object, Object>

so the super type is Object. 所以超级类型是Object。

I would back up, and ask yourself why you have a map that is storing both Maps and Strings. 我会备份并问自己,为什么您有一个同时存储Maps和Strings的映射。 You should probably refactor them into objects that actually represent their types. 您可能应该将它们重构为实际代表其类型的对象。

For instance, let's say it's a file system and the Map is a directory and the String is a File, then you should really create a FileObject that can be either a directory or a file, and your method signature becomes: 例如,假设它是一个文件系统,而Map是一个目录,而String是一个文件,那么您实际上应该创建一个既可以是目录也可以是文件的FileObject,并且您的方法签名变为:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM