1

我想确保在我的代码变得太大/太复杂而无法发布之前这将起作用。我没有足够的能力来测试这是否按我期望的方式工作

我正在做一些我想在 AST 上使用访问者模式的东西。accept(Visitor)我的目标是通过在超类中使用反射消除在每个子类中覆盖的需要,使访问者在实现新类型的 TreeNode 时几乎透明。

通过允许visit(TreeNode)它允许未知节点类型的默认方法,这样在添加新节点类型时不需要更改旧访问者。

类参数 R 和 P 是访问的返回值和参数,这是我在这个编程堆栈交换问题中学到的技巧。

为此,我有以下内容:

public abstract class TreeNode {
    public final < R, P > R accept(TreeVisitor<R,P> v, P p){
        try{
            Method m = v.getClass().getMethod("visit", getClass(),Object.class);
            return (R)m.invoke(v, this,p);
        } catch (IllegalAccessException ex) {
        } catch (IllegalArgumentException ex) {
        } catch (InvocationTargetException ex) {
        } catch (NoSuchMethodException nsme){
        }
        return (R)v.visit(this,p);
    }
    public abstract void contains(TreeNode n);//and other methods
}
//in another file
interface TreeVisitor<R,P> {
    public R visit(TreeNode n,P p);//default
    public R visit(TreeNodeSubclass tns,P p);
    //all other subclasses as well
}
//from here lower is un-tested, written just now, just for this post, code
//somewhere else we have an algorithm to visit nodes
class DoStuff implements TreeVisitor<String,Void>{
     public String visit(TreeNode n, Void v){
          return n.toString();
     }
     public String visit(TreeNodeSubclass n, Void v){
         return "SUB:" + n.toString();
     }
}

//algorithm in a method somewhere
DoStuff ds = new DoStuff();
for(TreeNode node : inOrderTraverse(ROOT_NODE)){
     node.accept(ds);
}

这会按我的预期工作吗(假设inOrderTraverse(ROOT_NODE)正确生成所有节点的列表)?

我的主要问题实际上是getMethod调用的部分,因为类型擦除Object.class应该是正确的参数,即使p.getClass()由于泛型参数而更喜欢使用P。然而,这不起作用,因为类型擦除导致访问者中的实际方法签名为Object visit(this.getClass(), Object)this.getClass()指的是我使用节点子类的实际类来获取访问者中正确的重载方法。

我对此的理解是正确的还是我遗漏了什么?

4

1 回答 1

1

如果您将 Object.class 作为参数类型传递,我不确定它是否会起作用,但我确实看到了另一个潜在问题:如果您的新节点具有私有而不是公共“访问”方法,那么您应该考虑它在异常中,例如:

try{
            Method m = v.getClass().getMethod("visit", getClass(),Object.class);
            return (R)m.invoke(v, this,p);
    } catch (NoSuchMethodException e) {
        try {
            Method m = v.getClass().getDeclaredMethod("visit", getClass(),Object.class);
            return (R)m.invoke(v, this,p);  
            } catch (Exception e){
            return (R)v.visit(this,p);//default
            }   
        } catch (Exception e){
            return (R)v.visit(this,p);//default
        }

于 2013-08-07T03:14:32.707 回答