Java:使用RuntimeException从访客转义
我正强烈地试图在Java程序中使用未经检查的异常作为短路控制流构造。 我希望这里的某个人能够以更好,更清洁的方式向我提出建议,以解决这个问题。
这个想法是,我想减少访问者对子树的递归探索,而不必在每个方法调用中检查“停止”标志。 具体来说,我使用抽象语法树上的访问者构建控制流图。 AST中的return
语句应停止对子树的探索,并将访问者发送回最近的包含if / then或loop块。
Visitor
超类(来自XTC库)定义
Object dispatch(Node n)
它通过表单的反射方法回调
Object visitNodeSubtype(Node n)
dispatch
没有声明抛出任何异常,所以我声明了一个扩展了RuntimeException
的私有类
private static class ReturnException extends RuntimeException {
}
现在,返回语句的visitor方法看起来像
Object visitReturnStatement(Node n) {
// handle return value assignment...
// add flow edge to exit node...
throw new ReturnException();
}
并且每个复合语句都需要处理ReturnException
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
,编译器不会警告我。 有一个更好的方法吗? 有没有Java模式我不知道实现这种非本地流量控制?
[UPDATE]这个具体的例子证明是有些无效的: Visitor
超类捕获并包装异常(即使是RuntimeException
),所以抛出异常并不会真正起作用。 我已经实现了建议,从visitReturnStatement
返回一个enum
类型。 幸运的是,这只需要在少数地方进行检查(例如visitCompoundStatement
),所以它实际上比抛出异常少一点麻烦。
总的来说,我认为这仍然是一个有效的问题。 虽然也许,如果你不与第三方库绑在一起,整个问题都可以通过合理的设计来避免。
我认为这是一个合理的方法,原因如下:
而且,有些人认为未经检查的例外情况并不是那么糟糕。 您的用法让我想起了Eclipse的OperationCanceledException,它用于清除长时间运行的后台任务。
这并不完美,但如果有详细记录,对我来说似乎就没有问题。
抛出运行时异常作为控制逻辑肯定是一个坏主意。 你感到肮脏的原因是你绕过了类型系统,即你的方法的返回类型是谎言。
你有几个更清洁的选项。
1.例外函子
一个很好的技巧,当你限制你可能抛出的异常时,如果你不能抛出一个检查的异常,则返回一个会抛出一个检查异常的对象。 例如,java.util.concurrent.Callable是此仿函数的一个实例。
在这里看到这个技术的详细解释。
例如,而不是这个:
public Something visit(Node n) {
if (n.someting())
return new Something();
else
throw new Error("Remember to catch me!");
}
做这个:
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!");
}
};
}
2.不相交的联盟(又名双联组合)
这种技术可以让你从同一种方法返回两种不同类型之一。 这有点像Tuple<A, B>
技术,大多数人都熟悉从方法中返回多个值。 但是,不是返回A和B类型的值,而是返回A或B类型的单个值。
例如,给定枚举失败,可能枚举适用的错误代码,该示例变为...
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);
}
打电话现在更清洁,因为你不需要try / catch:
Either<Fail, Something> x = node.dispatch(visitor);
for (Something s : x.rightProjection()) {
// Do something with Something
}
for (Fail f : x.leftProjection()) {
// Handle failure
}
Either类不是很难编写,但Functional Java库提供了一个全功能的实现。
3.选项Monad
有点像类型安全的null,当你不想为某些输入返回值时,这是一种很好的技巧,但不需要异常或错误代码。 通常情况下,人们会返回所谓的“定点值”,但选项相当干净。
你现在有...
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.
}
事实上,它是一个monad,可以让你连接呼叫而不处理特殊的None值:
public Option<Something> visit(final Node n) {
return dispatch(getIfPart(n).orElse(dispatch(getElsePart(n)));
}
Option类比Either更容易编写,但功能性Java库提供了全功能的实现。
请参阅此处以了解Option和Either的详细讨论。
是否有一个原因,你不只是返回一个值? 比如NULL,如果你真的想要什么都不返回? 这将会简单得多,并且不会冒着抛出未经检查的运行时异常的风险。
链接地址: http://www.djcxy.com/p/47687.html上一篇: Java: using a RuntimeException to escape from a Visitor