Java泛型递归HashMap类型安全警告

5

我正在使用一个哈希映射的递归树,具体来说是 Hashmap map,其中 Object 是对另一个哈希映射的引用,依此类推。这将在递归算法中传递:

foo(String filename, Hashmap<String, Object> map)
{
    //some stuff here
    for (Entry<String, Object> entry : map.entrySet()) 
    {
       //type warning that must be suppressed
       foo(entry.getKey(), (HashMap<String, Object>)entry.getValue());
    }
}

我确定 Object 的类型是 Hashmap<String, Object>,但我很烦恼必须使用 @SuppressWarnings("unchecked") 来抑制警告。
我希望能有一个解决方案,可以通过 assert(/*entry.getValue() is of type HashMap<String, Object>*/) 或在不是这种类型时抛出异常来满足我的需求。我选择了泛型路线以实现编译时类型安全,如果我忽略了警告,那就失去了这个目的。
感谢您的评论, ksb
3个回答

5
您可以使用这个类来替代HashMap:
public class RecursiveHashMap extends HashMap<String,RecursiveHashMap>
{
}

亲爱的Ha,不是很明白为什么这可能有效(Java新手),因此没有尝试过。我会选择waxwing的Node解决方案。谢谢, KSB - GC.
是的,创建“Node”类(又称组合模式)比“HashMap”的方式要好得多。 - Ha.

5

这可以使用递归类型变量的通用方法来实现。请尝试以下方法:

public <T extends Map<String, T>> void foo(String filename, T map) {
    //some stuff here
    for (Map.Entry<String, T> entry : map.entrySet())  {
        foo(entry.getKey(), entry.getValue());
    }
}

应该可以正常编译,没有任何警告。

然而,如果你控制地图并且能够替换自己的类,那么创建一个包含 Map 的 Node 类可能更易读(这对我来说看起来像一棵树)。例如:

public class Node {
    private Map<String, Node> children;

    ...
    // accessor methods to retrieve children ...
}

建议让foo的第二个参数接受一个Node。仅供参考。


亲爱的Waxwing,最终我采用了你的第二个建议,因为我需要在“节点”中添加比只是对另一个哈希映射的引用更多的东西。这也感觉更自然。我刚开始接触Java,所以无法理解你的第一个建议 - “T extends Map<String,T>”部分。再次感谢您, KSB - GC.

1

你的数据结构看起来像是想用它来表示文件(文件名)的树形结构。我不建议使用HashMap作为节点类型来实现这个目的。

我建议使用组合模式(参见维基百科),简化代码:

abstract class Node
{
  String filename;
  Node( String filename ) { this.filename = filename; }
  abstract foo();
}

class FileNode implements Node
{
  FileNode( String filename ) { super(filename); }
  foo() { ... }
}

class DirectoryNode implements Node 
{
  Set<Node> children;
  DirectoryNode( String filename, Set<Node> children )
  {
    super(filename);
    this.children = children;
  }
  foo()
  {
    for ( Node child : children ) child.foo();
  }
}

你之前使用的HashMap实际上就是DirectoryNode中出现的Set。


谢谢您的建议。我的实体无法自然地分成两种节点类型,即目录和文件。它们全部都是“文件”。具体来说,我需要在层次结构的所有层次上执行相同的处理foo()。我无法扩展您的代码以完成此操作。 - GC.
为什么不呢?你可以将所有需要的代码放入DirectoryNode.foo()中。你还可以将代码放入Node.foo()中,然后在DirectoryNode.foo()上调用super.foo()。这样,Node就会成为你的File。 - Wolfgang
我明白你的意思:将通用代码放在Node中(foo不再是抽象的),然后让File和Directory调用super(filename)。我会记住这个方法的。谢谢,ksb。 - GC.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接