Java:使用RuntimeException从访问者中转义

Java:使用RuntimeException从访问者中转义,java,exception,visitor,callcc,Java,Exception,Visitor,Callcc,我很想在Java程序中使用未检查的异常作为短路控制流构造。我希望这里有人能给我一个更好、更干净的方法来处理这个问题 我的想法是,我想缩短访问者对子树的递归探索,而不必在每个方法调用中检查“stop”标志。具体地说,我正在使用抽象语法树上的访问者构建一个控制流图。AST中的return语句应停止对子树的探索,并将访问者发送回最近的封闭if/then或循环块 Visitor超类(来自)定义 它通过表单的反射方法调用 Object visitNodeSubtype(Node n) dispatch没

我很想在Java程序中使用未检查的异常作为短路控制流构造。我希望这里有人能给我一个更好、更干净的方法来处理这个问题

我的想法是,我想缩短访问者对子树的递归探索,而不必在每个方法调用中检查“stop”标志。具体地说,我正在使用抽象语法树上的访问者构建一个控制流图。AST中的
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
    ,编译器不会警告我
  • 我觉得很脏
  • 有更好的方法吗?是否有一种我不知道的Java模式可以实现这种非本地控制流

    [更新]这个特定的例子有些无效:
    Visitor
    超类捕获并包装异常(甚至
    RuntimeException
    s),因此异常抛出并没有真正的帮助。我已经实现了从
    visitReturnStatement
    返回
    enum
    类型的建议。幸运的是,这只需要在少数地方进行检查(例如,
    visitCompoundStatement
    ),因此它实际上比抛出异常少了一点麻烦


    总的来说,我认为这仍然是一个有效的问题。不过,如果您没有绑定到第三方库,那么整个问题都可以通过合理的设计来避免。

    是否有理由不只是返回值?例如NULL,如果您真的不想返回任何内容?这将简单得多,并且不会冒引发未经检查的运行时异常的风险。

    我为您提供了以下选项:

  • 继续定义
    RuntimeException
    子类。通过在对
    dispatch
    的最常规调用中捕获您的异常,并在异常严重时报告,检查是否存在严重问题
  • 如果节点处理代码认为搜索应该突然结束,则让它返回一个特殊对象。这仍然迫使您检查返回值,而不是捕获异常,但是您可能更喜欢这样的代码外观
  • 如果树行走将被某些外部因素停止,则在子线程内执行所有操作,并在该对象中设置一个synchronized字段,以便通知线程提前停止

  • 我认为这是一个合理的方法,原因如下:

    • 您正在使用第三方,无法添加选中的异常
    • 在一大组访问者中检查返回值,而在少数情况下则是不必要的负担
    还有一些人认为。您的使用让我想起了Eclipse,它用于完成长期运行的后台任务


    它并不完美,但是,如果有很好的文档记录,我觉得它还可以。

    为什么要从访问者那里返回值?被访问的类调用访问者的适当方法。所有完成的工作都封装在visitor类本身中,它不应返回任何内容并处理自己的错误。调用类所需的唯一义务是调用适当的visitXXX方法,仅此而已。(这假设您使用的是示例中的重载方法,而不是为每种类型重写相同的visit()方法)

    被访问的类不应该被访问者改变,也不应该知道它做了什么,除非它允许访问发生。返回值或引发异常将违反此规则


    您必须使用XTC的访问者吗?这是一个非常简单的接口,您可以实现自己的接口,它可以抛出checkedReturnException,您不会忘记在需要的地方捕获它。

    我没有使用您提到的XTC库。它如何提供访问者模式的补充部分—节点上的
    accept(visitor)
    方法?即使这是一个基于反射的调度器,也必须有一些东西来处理语法树下的递归

    如果这个结构化的迭代代码很容易访问,并且您还没有使用
    visitXxx(node)
    方法的返回值,那么您可以利用一个简单的枚举返回值,或者甚至一个布尔标志,告诉
    accept(visitor)
    不要递归到子节点中吗

    如果:

    • accept(visitor)
      不是由节点显式实现的(存在一些字段或访问器反射,或者节点只是为一些标准控制流逻辑实现一个子获取接口,或者出于任何其他原因……),以及

    • 您不想弄乱库的结构迭代部分,或者它不可用,或者不值得付出努力

    那么,作为最后的手段,我想在仍然使用vanilla XTC库的情况下,异常可能是您唯一的选择


    不过这是一个有趣的问题,我可以理解为什么基于异常的控制流会让您感觉脏兮兮的…

    将运行时异常作为控制逻辑抛出肯定是个坏主意。您感到脏的原因是您绕过了类型系统,即方法的返回类型是谎言

    你有几个选择,这是相当干净

    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)));
    }