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?

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.

  • 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 '14 at 14:42
  • @Mash Sorry about that, see my edit. – Doval May 20 '14 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 '14 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 '14 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 '14 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.

  • 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 '14 at 14:21
  • It will re-throw whatever exception was originally thrown. – Matthew May 20 '14 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 '14 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 '14 at 15:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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