【问题标题】:Java Generics with Reflection for Visitor Pattern具有访问者模式反射的 Java 泛型
【发布时间】:2016-02-14 08:15:07
【问题描述】:

我想确保在我的代码变得太大/太复杂而无法发布之前,这将起作用。我没有足够的时间来测试这是否符合我的预期

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

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

类Parameters 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 而更喜欢使用 p.getClass()。但是这不起作用,因为类型擦除导致访问者中的实际方法签名为Object visit(this.getClass(), Object)this.getClass() 指的是我正在使用节点的子类的实际类来获得正确的重载访问者中的方法。

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

【问题讨论】:

    标签: java generics reflection visitor-pattern


    【解决方案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
            }
    

    【讨论】:

    • 他们应该有一个公共方法,因为他们将实现访问者接口。此外,Exception e 对于我来说可能不是默认情况下最好的选择。 (编辑 OP 以反映这一点)
    猜你喜欢
    • 1970-01-01
    • 2022-12-04
    • 1970-01-01
    • 2021-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多