Java 抛出异常强制退出递归有效吗?

Java 抛出异常强制退出递归有效吗?,java,recursion,tail-recursion,Java,Recursion,Tail Recursion,以下只是伪代码 function x(node){ if(node.val == 42) return true; // What if I throw an exception here ? val1 = node.left ? x(node.left) : false ; val2 = node.right ? x(node.right): false; return val1 || val2 ; } 假设我在树上找42个。对于上面的

以下只是伪代码

  function x(node){
    if(node.val == 42) 
       return true; // What if I throw an exception here ?
    val1 = node.left ? x(node.left) : false ;
    val2 = node.right ? x(node.right): false; 
    return val1 || val2 ;
 }
假设我在树上找42个。对于上面的代码,如果我返回一个有效值,它将冒泡到整个递归链,然后最终返回

我的假设是,如果我只是抛出一个异常,而不是在第3行返回true,那么它实际上不会在递归链中冒泡出来,直接返回调用方

抛出异常以中断整个链是否是一种好的做法。因为让我们假设在
第4行
,它确实在某个嵌套堆栈中找到了42个,并且它仍然会在
第5行
处执行递归。如果我只是在第3行抛出一个异常,我们可以避免不必要的计算


问题更像是编译器内部的问题,如果我只是抛出一个未处理的异常,它是否仍会冒泡到递归堆栈(这使得这种方法毫无意义)或直接返回到父调用的程序计数器。

出于几个不同的原因,我不建议抛出异常来打破递归链

  • 效率。一般来说,抛出异常比从函数返回要慢得多。具体来说,当抛出异常时,运行时需要使用当前堆栈跟踪填充异常,需要查看要调用的处理程序,需要搜索要执行的
    最后
    块或
    尝试
    -使用resources语句,跟踪对象引用以进行垃圾收集,这仍然需要遍历调用堆栈,就像在调用链上返回值一样,而且几乎肯定不会比更标准的方法快

  • 可用性。如果您抛出异常以中止递归链,那么调用您的函数的任何人都需要编写对该异常作出反应的代码。这意味着它们需要有单独的分支,一个用于调用正常返回值的分支,另一个用于调用抛出值的分支,而不是编写
    if(myCall()){…}
    。如果他们忘记了这样做,那么运行时的代码可能会因为异常而失败,而实际上它的行为与预期的一样。更糟糕的是,您实际上将反转异常的正常使用。如果您的代码在成功时抛出异常,在失败时返回值,那么在理解您的意思之前,阅读您代码的人可能至少会“嗯?”一次

  • 清晰度。例外情况应该用于例外情况!异常机制设计用于在没有可行的方法返回值的情况下报告信息。重新调整异常系统的用途以处理常规控制流使代码更难阅读和维护,并且违反了最小意外原则


虽然你最初的问题主要是关于效率的问题,但我认为其他两点——可用性和清晰性——仍然非常有利于不这样做。

一个学生问我这个。。这个问题让我很不安。如果你能描述一下为什么你认为这是个好主意,那会很有用。我看不出它有什么好处,所以很难知道从哪里开始。异常应该用来处理“异常”场景,所以用它们控制流不是一个好主意。通常,有一个更优雅的解决方案。你能张贴一些代码考虑吗?这是我的资源,每当出现:注意:抛出的另一个代价是调用本机fillInStackTrace(),所以如果你真的想这样爆发,就尽量避免这种情况。你为什么要抛出呢?只要在找到“有效结果”时返回,就不会重复太多。这就是全部要点:如果参数为true,则返回的堆栈推送次数与返回效率点的堆栈推送次数完全相同。如果编译器仍然需要遍历整个堆栈跟踪,那么这种方法确实没有意义。@sapy我认为,即使它更有效,您仍然需要以某种方式克服其他障碍。为了提高效率而对代码进行的大量修改会妨碍可读性,因此仍需要进行成本效益分析。