0

Let's say I have a function named foo(). Within foo() there is a try-catch-finally block. Inside the catch block, foo() is called recursively.

My question is:

How can I have the finally block only execute once, on the original function call?

I want to limit the number of recursive calls that can be made by simply using a counter (an Integer that I increment). Please see the example code below and you will have a generic understanding of what I am trying to accomplish:

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

Unless I am mistaken, the finally block can potentially be called multiple times, which I do not want to happen.

If you believe there is a better way to accomplish this (e.g., using something other than an incrementing integer, etc.), then please share your thoughts.

Sprag
  • 69
  • 11
  • Every time that `try` block is entered, the `finally` block **must** also be entered. Time to rethink your design. – Elliott Frisch Apr 19 '19 at 03:26
  • This seems like an [xy problem](https://meta.stackexchange.com/q/66377/243725); you want to limit something to five reattempts. Use an ordinary loop. No recursion required. Also Netflix's [Hystrix](https://github.com/Netflix/Hystrix) is a better way to accomplish this (imo). – Elliott Frisch Apr 19 '19 at 03:37
  • Thank you for your thoughts so far. What about "wrapping" foo() in another function. Let's call it wrappedFoo(). It simply calls the original foo(), then after foo() returns, I could call resetRecursionCounter() inside the wrappedFoo() function. I would also need to put resetRecursionCounter() inside the catch block if statement. I think this will work, though I'm not sure if it would be frowned upon. – Sprag Apr 19 '19 at 03:50

3 Answers3

2

Simply don’t use recursion for such a task:

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

Just for completeness, if you’re going to solve a task via recursion, don’t use instance fields to hold the local state of the recursion. When you keep local what is supposed to be local, there is no persistent state that needs a reset. (And, by the way, don’t use Integer objects where int values are sufficient)

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);
        }
    }
}
Holger
  • 285,553
  • 42
  • 434
  • 765
1

Initially, method foo() must be invoked from outside of that method. That's your initial call and that's where your try-catch should be.

Pseudo code. Uncompiled and untested.

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();
    }
}
Abra
  • 19,142
  • 7
  • 29
  • 41
  • This is true, but if foo() is called in many places, it is quite inconvenient to write a try catch in every place it is called. This is a good backup solution, but I'm trying to maximize my laziness. :) – Sprag Apr 19 '19 at 03:57
  • If `FileInputStream` constructor is called in many places, is it inconvenient to handle the `FileNotFoundException` that it throws? – Abra Apr 19 '19 at 04:00
  • Generic opinion: If the exception can be handled in one place instead of many places, then yes, I would say it is inconvenient to use the solution where you handle the exception in many places. Otherwise, if it cannot be handled in one place, then it is not inconvenient, since that is your only option. I think the answer to your question depends on the context. – Sprag Apr 19 '19 at 04:04
0

One simple way: instead of a member variable for recursioncount, make attemptNumber an extra argument to foo, with default=1. (Overloaded function technically, since Java doesn't do default parameters)

I think carrying this information along with the function call makes a bit more sense than keeping it as instance state-- which is not thread-safe BTW

Patrick Parker
  • 4,863
  • 4
  • 19
  • 51