3

I am looking for a way to get the logic of retrying an operation in a single method while keeping the exception types of the operation.

I.e., the implementation to retry an operation could look like this:

public void retriedOperation(Operable operable, int maxAttempts) throws Exception {
    for (int attempt = 0; attempt < maxAttempts; attempt++) {
        try {
            operable.doOperation();
            break;
        } catch (Exception e) {
            if (attempt + 1 > maxAttempts) {
                throw e;
            }
        }
    }
}

This implementation is however only capable of throwing an unspecified Exception and thereby looses lots of information.

Unfortunately, generic Exceptions can neither be caught nor thrown not be caught in Java, thus something like

public void <ExceptionType extends Exception> retriedOperation(...) throws ExceptionType { ... }

would only allow to catch an unspecific Exception, check if it is of the right type and then cast it. But if it is of another type, it cannot be thrown with this method header.

Is there a way to implement a generic retry function that preserves specific exceptions?

2 Answers 2

5

Unfortunately, generic Exceptions can neither be caught nor thrown in Java, thus something like ... is not possible.

Change that to:

public void <T extends Exception> retriedOperation(...) throws T { ... }

And it should work just fine. When T is a checked exception, the compiler will complain appropriately. E.g.

obj.<IOException>retriedOperation(...);

Will force the surrounding method to either handle IOException or declare that it throws it. However, as long as Operable specifies that it throws Exception, your code won't work, since the method would have to check if the caught exception is of a particular type it doesn't yet know. The real solution is to change Operable to specify whatever exception it throws:

class Operable<T extends Exception>

And now you can rewrite your method as

public <T extends Exception> void retriedOperation(Operable<T> operable, ...) throws T {
    ...
    } catch (Exception e) {
        if (e instanceof RuntimeException) {
            throw (RuntimeException) e;
        } else {
            throw (T) e;
        }
    }
    ...
 }

And everything will work just fine, provided Operable only throws one kind of checked exception. If you need to handle more than one, the solution will get a bit ugly - you'll have to pass a list of class tokens as an argument and check if the caught exception is of a compatible type for any of them. (In retrospect, this wouldn't work as described. You do need class tokens, but you'd need an overload of the method for 2 exceptions, 3 exceptions, etc to get the throws clause right.)

Finally, it seems to me retriedOperation should be a static method.

10
  • I actually tried it that way, but there is still an unchecked cast involved: since an Exception of generic type cannot be caught, an unspecified Exception needs to be caught and then cast to the generic T type. This presents a problem when the Exception is not of type T because we cannot throw it.
    – mschrimpf
    May 20, 2014 at 14:42
  • @Mash Sorry about that, see my edit.
    – Doval
    May 20, 2014 at 14:49
  • I think, there should also be another catch-block that catches RuntimeExceptions and re-throws them before the catch-block with the general Exception.
    – mschrimpf
    May 20, 2014 at 14:54
  • See my new edit. If you catch Exception and cast, it'll work, provided the caught exception is of the advertised type.
    – Doval
    May 20, 2014 at 14:57
  • Yes but since the Operable<T> is still capable of throwing unchecked RuntimeExceptions that are not of the generic type, these should be handled explicitly to avoid cast-errors.
    – mschrimpf
    May 20, 2014 at 15:00
-1

I'm not a java developer, but this might work for you anyway.

You can try using the instanceof operator.

public void retriedOperation<T>(Operable operable, int maxAttempts) throws Exception {
    for (int attempt = 0; attempt < maxAttempts; attempt++) {
        try {
            operable.doOperation();
            break;
        } catch (Exception e) {
            if (!(e instanceof T) || attempt + 1 > maxAttempts) {
                throw e;
            }
        }
    }
}

Where T is the type of the exception you want to allow.

4
  • That's better in the way that only specific exceptions get caught. Unfortunately, it's still an unspecific Exception that gets thrown.
    – mschrimpf
    May 20, 2014 at 14:21
  • It will re-throw whatever exception was originally thrown.
    – Matthew
    May 20, 2014 at 14:31
  • @Matthew: Because of Java's checked exceptions, specifying "throws Exception" requires the /call site/ to handle Exception, too. The moment you have anything that throws Exception you have to break the exception model to write sane code.
    – Phoshi
    May 20, 2014 at 14:39
  • 1
    That won't work. Java's type erasure means you can't use a generic parameter for instantiating objects, getting the Class information, or checking type with instanceof at runrime...
    – Idan Arye
    May 20, 2014 at 15:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.