A few specific catch or one catches everyone?

I saw how this topic sometimes appeared in the past, but even after Googling about it , I still canโ€™t understand what being a good and elegant way to deal with it, so here it is.

Say I have code that throws various exceptions ...

try { /* some code that throws these exceptions */ } catch (NoSuchAuthorityCodeException e) { throw new MyAPIException("Something went wrong", e); } catch (FactoryException e) { throw new MyAPIException("Something went wrong", e); } catch (MismatchedDimensionException e) { throw new MyAPIException("Something went wrong", e); } catch (TransformException e) { throw new MyAPIException("Something went wrong", e); } 

... and, as we can see, I just transfer these exceptions and throw a new one, saying that something went wrong in my API.

It seems to me that the code is too repetitive, so you just catch the type of exception and process it with a shell and throw away a new one.

 try { /* some code that throws these exceptions */ } catch (Exception e) { throw new MyAPIException("Something went wrong", e); } 

In this case, it will be much simpler, but the fact is that we will also catch every RuntimeException. Given this, we could catch a RuntimeException to avoid this.

 try { /* some code that throws some exceptions */ } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new MyAPIException("Something went wrong", e); } 

Its a little cumbersome, but it does the trick. Now one more minor problem with catch (Exception e) is that if my internal API throws another MyAPIException, it will also be caught, wrapped and thrown into another MyAPIException. In this particular case, we could also catch a MyAPIException and throw it again.

 try { /* some code that throws some exceptions */ } catch (RuntimeException e) { throw e; } catch (MyAPIException e) { throw e; } catch (Exception e) { throw new MyAPIException("Something went wrong", e); } 

Well, it gets dirty again, but in this case we prevent a MyAPIException wrap and just throw it again. But, in addition, there is another problem with the catch (Exception e) block, which, if the internal API changes and starts throwing a different kind of exception (some other than the above 4), the compiler did not say anything about it, and we would have there is a key. Not that this was a serious problem, since I probably would have looked at it the same way.

In this scenario, I think the question is which one is better, and are there any better options?

+7
java exception-handling
source share
6 answers

Since you are stuck in Java5 and using your own exception, why not put all this exception logic in the exception class.

Useage

 try { // some code that might throw one of several exceptions } catch ( Exception cause ) { MyAPIException . handle ( cause ) ; } 

MyAPIException contains logic

 class MyAPIException extends Exception { private MyAPIException ( String message , Throwable cause ) { super ( message , cause ) ; } private static void myAPIException ( Exception cause ) throws MyAPIException { throw new MyAPIException ( "Something Went Wrong" , cause ) ; } public static void handle ( Exception e ) throws MyAPIException { try { throw ( e ) ; } catch ( RuntimeException cause ) { throw cause ; } catch ( MyAPIException cause ) { throw cause ; } catch ( NoSuchAuthorityCodeException cause ) // repeat for other exceptions { myAPIException ( cause ) ; } catch ( Exception cause ) // this should not happen { assert false ; // or log it or throw a RuntimeException ... somehow signal that programming logic has failed } } } 
+5
source share

Have more than one MyApiException . If you have all exceptions like MyApiException , it will be a little difficult for you, and for others to read your code. Properly naming it will also be crucial. In addition, you do not always want to catch and remodel it. If so, just declare throws in the method signature.

In addition, you cannot fix any of the simple catches or multiple catches. Its a more balanced IMO solution, since there are exceptions that are fatal (as in the whole program, you have to stop it) to easier-to-use types.

I don't see anything wrong when you have a bunch of throws suggestions in your API. This is a much tidier implementation. So what to do if the caller needs to handle the exceptions that you have identified. If the method in your API can throw an exception, and something needs to be done about this, then the caller should handle it. In any case, your documentation of this particular exception should be clear so as not to confuse the caller.

Another factor that solves this is API complexity. Some time ago, I wrote an API that was moderately complex, and I had to deal with the same problems as you. I wrote some special exceptions for several methods. I am sure that you will not throw exceptions for the entire public method that the API provides, but there are places that are inevitable.

Finally, if you feel that too many special exceptions are a bitter choice, you may have one exception with clearly documented error codes. In this way, the caller can handle the exception and process the error codes as he sees fit.

+2
source share

In JAVA 7 you can do something like

 try { /* some code that throws these exceptions */ } catch (NoSuchAuthorityCodeException , FactoryException, MismatchedDimensionException , TransformException e) { throw new MyAPIException("Something went wrong", e); } 

So, the best answer is to relax a bit and then upgrade to JAVA 7 when it becomes available.

+2
source share

OK guys, that's what I came up with ... It's not that elegant, but I think a little better than with different catches.

First, we have an abstract class ExceptionHandler .

 package my.api.pack; import java.lang.reflect.ParameterizedType; import java.util.LinkedHashSet; import java.util.Set; public abstract class ExceptionHandler<E extends Exception> { Set<Class<? extends Exception>> exceptionClasses = new LinkedHashSet<Class<? extends Exception>>(); protected abstract void handler(Throwable t) throws E; public ExceptionHandler<E> catches(Class<? extends Exception> clazz) { exceptionClasses.add(clazz); return this; } @SuppressWarnings("unchecked") private Class<E> getGenericsClass() { ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass(); return (Class<E>) parameterizedType.getActualTypeArguments()[0]; } @SuppressWarnings("unchecked") public final void handle(Throwable t) throws E, UnhandledException { if (getGenericsClass().isInstance(t)) { throw (E) t; } for (Class<? extends Exception> clazz : exceptionClasses) { if (clazz.isInstance(t)) { handler(t); return; } } throw new UnhandledException("Unhandled exception", t); } } 

Along with this, we have this simple runtime exception called UnhandledException

 package my.api.pack; public class UnhandledException extends RuntimeException { private static final long serialVersionUID = -3187734714363068446L; public UnhandledException(String message, Throwable cause) { super(message, cause); } } 

With it, we can use them to handle exceptions like this ...

 try { /* some code that throws these exceptions */ } catch (Exception e) { new ExceptionHandler<MyAPIException>() { @Override protected void handler(Throwable t) throws MyAPIException { throw new MyAPIException("Something went wrong", t); } }. catches(MismatchedDimensionException.class). catches(NoSuchAuthorityCodeException.class). catches(FactoryException.class). catches(TransformException.class). handle(e); return null; } 

What do you guys think?

+1
source share

It all depends on what you want to do. If all you want to do is throw new MyException("Something went wrong", e); then everything will catch.

0
source share

Below is a different approach and usage example. The MyAPIException class has a processing method. The descriptor method will handle any exception.

  • If the Exception is Runnable or MyAPIException, the handle will unload it without packing it.
  • If not, the handle method checks to see if statements are included. If statements are allowed, the handle method checks to see if an exception can be thrown from one of the supported exception types by throwing an AssertionError if it cannot be thrown. (If statements are not included, the handle method ignores the parameter of supported exception types.)
  • Finally - if it reaches this point - the descriptor method will wrap the exception in the MyAPIException exception and throw it.

When you check your code, run it with statements included. During production, run it with claims disabled.

<soap box> If you use this method, you will have to test errors that the compiler would otherwise catch. </soapbox>

 class MyAPIException extends Exception { private static final long serialVersionUID = 0 ; MyAPIException ( Throwable cause ) { super ( cause ) ; } static void handle ( Exception cause , Class < ? > ... supportedExceptionTypes ) throws MyAPIException { try { throw ( cause ) ; } catch ( RuntimeException e ) { throw ( e ) ; } catch ( MyAPIException e ) { throw ( e ) ; } catch ( Exception e ) { search : try { assert false ; } catch ( AssertionError a ) { for ( Class < ? > c : supportedExceptionTypes ) { if ( c . isAssignableFrom ( e . getClass ( ) ) ) { break search ; } } assert false : e + " is not one of the supportedExceptionTypes : " + supportedExceptionTypes ; } MyAPIException error = new MyAPIException ( e ) ; throw ( error ) ; } } } 

This is an example use. You can run it with statements enabled / disabled and with parameters 1,2,3,4 to see how it handles different situations.

 class Usage { public static void main ( String [ ] args ) throws MyAPIException { try { switch ( Integer . parseInt ( args [ 0 ] ) ) { case 1 : throw new RuntimeException ( ) ; case 2 : throw new SQLException ( ) ; case 3 : throw new MyAPIException ( null ) ; case 4 : throw new IOException ( ) ; } } catch ( Exception cause ) { System . out . println ( cause . getMessage ( ) ) ; System . out . println ( cause . getCause ( ) ) ; MyAPIException . handle ( cause , IOException . class ) ; } } } 
0
source share

All Articles