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?

asked May 20, 2014 at 14:12

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.

answered May 20, 2014 at 14:31
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. Commented May 20, 2014 at 14:42
  • @Mash Sorry about that, see my edit. Commented 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. Commented 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. Commented 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. Commented 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.

answered May 20, 2014 at 14:18
4
  • That's better in the way that only specific exceptions get caught. Unfortunately, it's still an unspecific Exception that gets thrown. Commented May 20, 2014 at 14:21
  • It will re-throw whatever exception was originally thrown. Commented 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. Commented 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... Commented May 20, 2014 at 15:41

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.