Reputation: 548
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
V newElement = myMethod(value);
-> Compile ErrorMap<K,V> newElement = myMethod(value);
->compile ErrorV newElement = (V) myMethod((Map<?,?>)value);
->Type safety warning--------- Edited ----------
The further assumption that I could make is
Upvotes: 1
Views: 643
Reputation: 122429
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()
; 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.
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
. 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
? Then it should not be able to cast to V
, and there is no way you can safely get a V
from that.
Upvotes: 2
Reputation: 1
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.
So you basically need String, Map, and V to all have some common super type. In the original code, Map is really
Map<Object, Object>
so the super type is Object.
I would back up, and ask yourself why you have a map that is storing both Maps and 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<K, FileObject> myMethod(Map<K, FileObject> map)
Upvotes: -1