具有访问者模式反射的Java泛型

具有访问者模式反射的Java泛型,java,generics,reflection,visitor,Java,Generics,Reflection,Visitor,我想在我的代码变得太大/太复杂而无法发布之前,确保它能正常工作。我没有足够的数据来测试这是否符合我的预期 我正在做一些事情,我想在AST上使用访问者模式。我的目标是通过在超类中使用反射,消除在每个子类中重写accept(visitor)的需要,使访问者在实现新型树节点时几乎是透明的 通过允许访问(TreeNode)它允许对未知节点类型使用默认方法,这样在添加新节点类型时就不需要更改旧访问者 类参数R和p是访问的返回值和参数,这是我在这里学到的技巧 为此,我有以下几点: public abstra

我想在我的代码变得太大/太复杂而无法发布之前,确保它能正常工作。我没有足够的数据来测试这是否符合我的预期

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

通过允许
访问(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);
}
公共抽象类树节点{
公开决赛R接受(TreeVisitor v,P){
试一试{
方法m=v.getClass().getMethod(“访问”,getClass(),Object.class);
返回(R)m.invoke(v,this,p);
}捕获(非法访问例外){
}捕获(IllegalArgumentException ex){
}捕获(调用TargetException ex){
}捕获(NoSuchMethodException nsme){
}
回访(R)v.访问(this,p);
}
公共抽象void包含(TreeNode n);//和其他方法
}
//在另一个文件中
接口树定位器{
公共R访问(treenoden,P);//默认值
公共R访问(TreeNodeSubclass tns,P);
//所有其他子类也是如此
}
//从这里开始,lower是未经测试的,刚刚编写的,只是为了这篇文章,代码
//在其他地方,我们有一个访问节点的算法
类DoStuff实现TreeVisitor{
公共字符串访问(TreeNode n,Void v){
返回n.toString();
}
公共字符串访问(TreeNodeSubclass n,Void v){
返回“SUB:+n.toString();
}
}
//某个方法中的算法
DoStuff ds=新的DoStuff();
for(树节点:inOrderTraverse(根节点)){
接受节点(ds);
}
这会像我预期的那样工作吗(假设
在orderTraverse(根节点)
中正确地生成所有节点的列表)

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


我对这一点的理解是正确的还是遗漏了什么?

如果您将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
        }

如果将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以反映这一点)他们应该有一个公共方法,因为他们将实现访问者界面。另外,
Exception e
对于我来说可能不是默认设置的最佳选择。(编辑OP以反映这一点)