Java泛型与访客模式的反思

时间:2013-08-07 02:10:20

标签: java generics reflection visitor

我想确保在我的代码太大/太复杂而无法发布之前这将有效。我没有足够的资料来测试这是否符合我的预期

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

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

类参数R和P是访问的返回值和参数,这是我在programming stack exchange question处获取的技巧。

要做到这一点,我有以下内容:

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()指的是我正在使用Node的子类的实际类来实现在访问者中获取正确的重载方法。

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

1 个答案:

答案 0 :(得分: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
        }

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 }