3

I was trying to have a handleException method, which can take an exception object and a list of acceptable exception classes to check if the exception is acceptable and can be retried.

void handleException(Exception e, String... acceptableExceptionNames)
      throws MyException {

    boolean isRetryable = false;

    for(String acceptableExceptionName: acceptableExceptionNames) {
      try {
        if (Class.forName(acceptableExceptionName).isInstance(e)) {
          isRetryable = true;
          break;
        }
      } catch (ClassNotFoundException e1) {
        continue;
      }
    }

    if (isRetryable) {
      // log retryable
    } else {
      // log error
    }

    throw new MyException(isRetryable, "Failed");
  }

The parameter I pass in is a String... classNames instead of Class<? extends Exception> classes, because if I do something like this:

void handleException(
    Exception e,
    Class<? extends Exception>... acceptableExceptions)
      throws MyException {
    for (Class acceptableException : acceptableExceptions) {
        if (e instanceOf acceptableException) {}
    }
}

The IDE will complain about unknown class acceptableException

Anyone knows if there's a way to pass Class<?>? Or a better way to avoid using String classNames and Class.forName()?

Mureinik
  • 297,002
  • 52
  • 306
  • 350
Freya Ren
  • 2,086
  • 6
  • 29
  • 39

4 Answers4

7

acceptableExceptions isn't a Class, it's a Class[]. You can keep your original design, though, and just use the Class objects directly instead of creating them from strings:

void handleException
        (Exception e, Class<? extends Exception>... acceptableExceptionNames)
        throws MyException {

    boolean isRetryable = false;

    for(Class<?> acceptableExceptionName: acceptableExceptionNames) {
        if (acceptableExceptionName.isInstance(e)) {
            isRetryable = true;
            break;
        }
    }

    if (isRetryable) {
        // log retryable
    } else {
        // log error
    }

    throw new MyException(isRetryable, "Failed");
}

EDIT:
As a side note, this code can be made considerably shorter using Java 8's streams:

void handleException
        (Exception e, Class<? extends Exception>... acceptableExceptionNames)
        throws MyException {

    boolean isRetryable = 
        Arrays.stream(acceptableExceptionNames).anyMatch(x -> x.isInstance(e));

    if (isRetryable) {
        // log retryable
    } else {
        // log error
    }

    throw new MyException(isRetryable, "Failed");
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • 2
    Why replace `Class extends Exception>` with `Class>`? You are no longer restricting the class types. – Andreas Jan 05 '17 at 19:59
  • @Andreas because `Class extends Exception>` will result in a heap-polution warning via `acceptableExceptionNames`, though I do not fully understand why this warning occurs. – Turing85 Jan 05 '17 at 20:13
  • What "heap-polution warning"? My Eclipse is giving no such warning. – Andreas Jan 05 '17 at 20:14
  • @Andreas `javac -d ..\bin Main.java -Xlint:unchecked` -> `Main.java:8: warning: [unchecked] Possible heap pollution from parameterized vararg type Class extends Exception>` – Turing85 Jan 05 '17 at 20:19
  • @Andreas Good point - no good reason, just wasn't paying attention when I copy-pasted and edited OP's code. Edited and fixed. – Mureinik Jan 05 '17 at 20:20
  • The heap pollution warning could be solved by making the method `final` and adding a `@SafeVarags` annotation to it. Didn't want to distract attention from the core of the answer, but this is a change that can easily be applied to the real code. – Mureinik Jan 05 '17 at 20:33
  • Thanks for this detailed answer! – Freya Ren Jan 05 '17 at 20:59
2

You don't want to check if e is an instance of a Class[], which is what acceptableExceptions is, but if it is an instance of one of the classes referenced by the acceptableExceptions array.

To do that, you need to iterate them, and you need to use the reflection method Class.isInstance(Object obj). As the javadoc says:

This method is the dynamic equivalent of the Java language instanceof operator.

To prevent compiler warnings, you also need to add @SafeVarargs to you method if it is static or final. Otherwise, you need to add @SuppressWarnings("unchecked") to both the method and it's caller(s).

@SuppressWarnings("unchecked")
void handleException(Exception e, Class<? extends Exception>... acceptableExceptions) throws MyException {
    boolean acceptable = false;
    for (Class<? extends Exception> acceptableException : acceptableExceptions)
        if (acceptableException.isInstance(e)) {
            acceptable = true;
            break;
        }
    if (acceptable) {
        // code here
    }
}
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Unfortunately, `@SafeVarargs` doesn't apply to non-final instance methods with varargs. If that is needed, you'll end up with [`@SuppressWarnings("unchecked")`](http://stackoverflow.com/questions/12462079/potential-heap-pollution-via-varargs-parameter). – dhke Jan 05 '17 at 20:05
  • @dhke You're right. I had made the method `static` in my testing. – Andreas Jan 05 '17 at 20:09
1

In my opinion, it's easier to do if by performing the simple String Comparisons like this:

private void handleException(Exception ex, String... acceptableException) {
    for (int x = 0; x < acceptableException.length; x++) {
        String[] exceptionClass = ex.getClass().toString().split(".");
        if (!acceptableException[x]
                .equals(exceptionClass[exceptionClass.length - 1])) {
            /* Exception Not Acceptable */
        }
    }

    /* Exception Acceptable */
}
user2004685
  • 9,548
  • 5
  • 37
  • 54
-1

Use following checking

for (Class<? extends Exception> exceptionClass : acceptableExceptions) {
    if (exceptionClass.isInstance(e)) {
        // it is your exception
    }
}
Dmitry Gorkovets
  • 2,208
  • 1
  • 10
  • 19