强制从递归中抛出异常是否有效?

时间:2019-03-30 21:26:50

标签: 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。使用上面的代码,如果我返回一个有效值,它将冒泡到整个递归链,然后最终返回。

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

只是抛出一个异常来破坏整个链条是一个好习惯。假设在line 4处,它确实在某个嵌套堆栈中找到了42个,并且仍然会在line 5处进行递归。如果我只是在line 3处引发异常,则可以避免不必要的计算。

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

strong>

1 个答案:

答案 0 :(得分:4)

出于某些不同的原因,我不建议抛出异常来破坏递归链。

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

  • 可用性。如果抛出异常以中止递归链,则调用函数的人都需要编写对该异常作出反应的代码。这意味着它们不必执行类似编写if (myCall()) {...}的操作,而是需要具有单独的分支,一个用于正常返回值的调用,另一个用于引发异常的值。如果他们忘记这样做,则运行时代码可能会由于异常而失败,而实际上却是按预期方式运行。使情况变得更糟的是,实际上您会反转异常的正常使用。如果您的代码在成功时抛出异常,并在失败失败时返回值,那么任何读取您代码的人都可能会““”?至少要弄清楚你的意思之前。

  • 清晰度。异常应该用于例外情况!异常机制旨在在根本没有可行的方法返回值的情况下报告信息。重新利用异常系统来处理常规控制流会使代码更难以阅读和维护,并且违反了“最少惊讶原则”。

尽管您最初的问题主要是关于效率点,但我认为其他两个点(可用性和清晰度)仍然强烈地支持不要以这种方式做事。