try-catch-finally 块中的递归:如何在原始函数调用中只调用 finally 块一次?

Recursion within a try-catch-finally block: How to only call the finally block one time, on the original function call?

假设我有一个名为 foo() 的函数。在 foo() 中有一个 try-catch-finally 块。在 catch 块内,递归调用 foo()。

我的问题是:

如何让 finally 块在原始函数调用时只执行一次?

我想限制可以通过简单地使用计数器(我递增的整数)进行的递归调用的次数。请查看下面的示例代码,您将对我要完成的工作有一个大致的了解:

private Integer recursion_counter = 0;

public ReturnType foo(){
    ReturnType returnType = new ReturnType();

    try{
        // Try to do something...
        returnType = doSomething();
    } catch (Exception e){
        if (recursion_counter == 5) {
            // Issue not fixed within 5 retries, throw the error
            throw e;
        } else {
            recursion_counter++;
            attemptToFixTheIssue();
            returnType = foo();
            return returnType;
        }
    } finally{
        resetRecursionCounter();
    }

    return returnType;
}

private void resetRecursionCounter(){
    recursion_counter = 0;
}

除非我弄错了,否则 finally 块可能会被多次调用,这是我不希望发生的。

如果您认为有更好的方法来完成此操作(例如,使用递增整数以外的其他方法等),请分享您的想法。

最初,方法 foo() 必须从该方法外部调用。那是你最初的电话,那是你的 try-catch 应该在的地方。

伪代码。未编译和未测试。

public static void main(String[] args) {
    try {
        ReturnType rt = foo();
    }
    catch (Exception e) {
    }
}

ReturnType foo() throws Exception {
    ReturnType returnType = new ReturnType();
    if (recursion_counter == 5) {
        throw new Exception();
    }
    else {
        foo();
    }
}

一种简单的方法:将 attemptNumber 作为 foo 的额外参数,而不是递归计数的成员变量,默认值为 1。 (技术上重载函数,因为 Java 不做默认参数)

我认为将此信息与函数调用一起携带比将其保留为实例状态更有意义——顺便说一句,这不是线程安全的

干脆不要对这样的任务使用递归:

public ReturnType foo() {
    for(int attempts = 0; ; attempts++) {
        try {
            // Try to do something...
            return doSomething();
        } catch(Exception e) {
            if(attempts == 5) {
                // Issue not fixed within 5 retries, throw the error
                throw e;
            } else {
                attemptToFixTheIssue();
            }
        }
    }
}

为了完整起见,如果您要通过递归解决任务,请不要使用实例字段来保存递归的本地状态。当您将应该是本地的东西保留在本地时,就没有需要重置的持久状态。 (顺便说一下,不要使用 Integer 对象,其中 int 值就足够了)

public ReturnType foo() {
    return foo(0);
}

private ReturnType foo(int recursionCounter) {
    try {
        // Try to do something...
        return doSomething();
    } catch (Exception e){
        if (recursionCounter == 5) {
            // Issue not fixed within 5 retries, throw the error
            throw e;
        } else {
            attemptToFixTheIssue();
            return foo(recursionCounter + 1);
        }
    }
}