我想确保在我的代码太大/太复杂而无法发布之前这将有效。我没有足够的资料来测试这是否符合我的预期
我正在研究一些我想在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的子类的实际类来实现在访问者中获取正确的重载方法。
我对此的理解是正确还是我遗漏了什么?
答案 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
}