Java:使用RuntimeException从访问者中转义
我很想在Java程序中使用未检查的异常作为短路控制流构造。我希望这里有人能给我一个更好、更干净的方法来处理这个问题 我的想法是,我想缩短访问者对子树的递归探索,而不必在每个方法调用中检查“stop”标志。具体地说,我正在使用抽象语法树上的访问者构建一个控制流图。AST中的Java:使用RuntimeException从访问者中转义,java,exception,visitor,callcc,Java,Exception,Visitor,Callcc,我很想在Java程序中使用未检查的异常作为短路控制流构造。我希望这里有人能给我一个更好、更干净的方法来处理这个问题 我的想法是,我想缩短访问者对子树的递归探索,而不必在每个方法调用中检查“stop”标志。具体地说,我正在使用抽象语法树上的访问者构建一个控制流图。AST中的return语句应停止对子树的探索,并将访问者发送回最近的封闭if/then或循环块 Visitor超类(来自)定义 它通过表单的反射方法调用 Object visitNodeSubtype(Node n) dispatch没
return
语句应停止对子树的探索,并将访问者发送回最近的封闭if/then或循环块
Visitor
超类(来自)定义
它通过表单的反射方法调用
Object visitNodeSubtype(Node n)
dispatch
没有声明抛出任何异常,因此我声明了一个扩展RuntimeException
private static class ReturnException extends RuntimeException {
}
Object visitIfElseStatement(Node n) {
Node test = n.getChild(0);
Node ifPart = n.getChild(1);
Node elsePart = n.getChild(2);
// add flow edges to if/else...
try{ dispatch(ifPart); } catch( ReturnException e ) { }
try{ dispatch(elsePart); } catch( ReturnException e ) { }
}
现在,return语句的visitor方法如下所示
Object visitReturnStatement(Node n) {
// handle return value assignment...
// add flow edge to exit node...
throw new ReturnException();
}
每个复合语句都需要处理ReturnException
private static class ReturnException extends RuntimeException {
}
Object visitIfElseStatement(Node n) {
Node test = n.getChild(0);
Node ifPart = n.getChild(1);
Node elsePart = n.getChild(2);
// add flow edges to if/else...
try{ dispatch(ifPart); } catch( ReturnException e ) { }
try{ dispatch(elsePart); } catch( ReturnException e ) { }
}
这一切都很好,除了:
ReturnException
,编译器不会警告我Visitor
超类捕获并包装异常(甚至RuntimeException
s),因此异常抛出并没有真正的帮助。我已经实现了从visitReturnStatement
返回enum
类型的建议。幸运的是,这只需要在少数地方进行检查(例如,visitCompoundStatement
),因此它实际上比抛出异常少了一点麻烦
总的来说,我认为这仍然是一个有效的问题。不过,如果您没有绑定到第三方库,那么整个问题都可以通过合理的设计来避免。是否有理由不只是返回值?例如NULL,如果您真的不想返回任何内容?这将简单得多,并且不会冒引发未经检查的运行时异常的风险。我为您提供了以下选项:
RuntimeException
子类。通过在对dispatch
的最常规调用中捕获您的异常,并在异常严重时报告,检查是否存在严重问题我认为这是一个合理的方法,原因如下:
- 您正在使用第三方,无法添加选中的异常
- 在一大组访问者中检查返回值,而在少数情况下则是不必要的负担
它并不完美,但是,如果有很好的文档记录,我觉得它还可以。为什么要从访问者那里返回值?被访问的类调用访问者的适当方法。所有完成的工作都封装在visitor类本身中,它不应返回任何内容并处理自己的错误。调用类所需的唯一义务是调用适当的visitXXX方法,仅此而已。(这假设您使用的是示例中的重载方法,而不是为每种类型重写相同的visit()方法) 被访问的类不应该被访问者改变,也不应该知道它做了什么,除非它允许访问发生。返回值或引发异常将违反此规则
您必须使用XTC的访问者吗?这是一个非常简单的接口,您可以实现自己的接口,它可以抛出checkedReturnException,您不会忘记在需要的地方捕获它。我没有使用您提到的XTC库。它如何提供访问者模式的补充部分—节点上的
accept(visitor)
方法?即使这是一个基于反射的调度器,也必须有一些东西来处理语法树下的递归
如果这个结构化的迭代代码很容易访问,并且您还没有使用visitXxx(node)
方法的返回值,那么您可以利用一个简单的枚举返回值,或者甚至一个布尔标志,告诉accept(visitor)
不要递归到子节点中吗
如果:
不是由节点显式实现的(存在一些字段或访问器反射,或者节点只是为一些标准控制流逻辑实现一个子获取接口,或者出于任何其他原因……),以及accept(visitor)
- 您不想弄乱库的结构迭代部分,或者它不可用,或者不值得付出努力
不过这是一个有趣的问题,我可以理解为什么基于异常的控制流会让您感觉脏兮兮的…将运行时异常作为控制逻辑抛出肯定是个坏主意。您感到脏的原因是您绕过了类型系统,即方法的返回类型是谎言 你有几个选择,这是相当干净 1。异常函子 一个很好的使用技巧,w
public Callable<Something> visit(final Node n) {
return new Callable<Something>() {
public Something call() throws Exception {
if (n.something())
return new Something();
else
throw new Exception("Unforgettable!");
}
};
}
public Either<Fail, Something> visit(final Node n) {
if (n.something())
return Either.<Fail, Something>right(new Something());
else
return Either.<Fail, Something>left(Fail.DONE);
}
Either<Fail, Something> x = node.dispatch(visitor);
for (Something s : x.rightProjection()) {
// Do something with Something
}
for (Fail f : x.leftProjection()) {
// Handle failure
}
public Option<Something> visit(final Node n) {
if (n.something())
return Option.some(new Something());
else
return Option.<Something>none();
}
Option<Something> s = node.dispatch(visitor));
if (s.isSome()) {
Something x = s.some();
// Do something with x.
}
else {
// Handle None.
}
public Option<Something> visit(final Node n) {
return dispatch(getIfPart(n).orElse(dispatch(getElsePart(n)));
}