我正在处理下面的遗留代码
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;
}
我的问题是关于递归调用的行。到目前为止,我已经尝试了
V newElement = myMethod(value);
- &gt;编译错误Map<K,V> newElement = myMethod(value);
- &gt;编译错误V newElement = (V) myMethod((Map<?,?>)value);
- &gt;类型安全警告---------编辑----------
我可以做的进一步假设是
答案 0 :(得分:2)
您的代码逻辑不是类型安全的,因此无法避免类型安全警告。
您使用createMap()
创建新地图;该方法盲目地创建一个新地图,没有任何信息,因此它创建的地图类不一定与传入的arg
类相同。这意味着您的方法myMethod()
返回一个映射不一定是与传入的映射相同的实现类。
在递归调用中,您将我们知道的V
实例的地图传递给递归调用,并且您希望返回的内容为V
。但这不一定是真的。例如,如果V
为TreeMap
,并且递归调用返回的内容是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)