简体   繁体   中英

Exception handling best practices revised

I have a piece of code where I capture all exceptions and throw one generic exception at the end. Something like this:

try {
  // do something here
} catch (Whatever e) {
  throw new MyException(e.getMessage());
}

This kind of makes my function definition looks clean, ie "myFunc throws MyException" but at the same time I lose the semantics of what caused the problem. On the other hand if I just throw all exceptions, that will make the function body cleaner but the definition of the function will contain 1-5 throw statements.

My question is: what is better?... should I capture all exceptions, keeping the function definition clean, or should I throw everything keeping the body of the function clean?

NOTE: the second approach will also make the exception handling code for my function more difficult...

It's usually a judgment call. Your function should expose exceptions relevant to the layer that it's in. Frequently this means passing through ones raised by the things you call, sometimes it doesn't.

For instance, if you have a getAThingy function and your application can be configured such that the thingy in question can come from a database or from a file, it's probably not appropriate for getAThingy to raise SqlException in the database case, because then code calling it has to handle that whether configured for files or database. (It would become a leaky abstraction .) So that would be an example of when you should probably catch and throw something else more appropriate.

In contrast, if you accept an identifier of some kind that shouldn't be null and someone passes in null , it seems perfectly appropriate to happily pass on the NullPointerException without wrapping it.

There's nothing wrong with a function throwing 10 different kind of exception, as long as each exception makes sense in the context.

If you just want to detect that something went wrong but you don't need to know exactly what, you can stick with your approach (ie throwing only one exception).

On the other hand, if you need to react to the different problems, you should keep the original exception.

There's no "better" approach, this depends on the context and the informations you need to have on the upper level. And you shouldn't worry about keeping the body or the function prototype "clean", both of the solution are "clean". The only important decision factor is the need of information.

Why pick one or the other?

try { /* doing useful work */ }
catch (Whatever t) { throw new MyException(t); }

where MyException has a constructor taking a Throwable as a parameter.

In general, I agree with what Krtek and TJ Crowder has said; throw exceptions that make sense at the level of abstraction you're working with, whether that means you list one type of exception, or ten.

The advice that I've heard on exceptions is to handle them as close to the place that they were thrown as possible. So, for example, if you can recover from an exception do so in the method where it was thrown and don't propegate the exception at all. If you cannot recover from the exception but code a layer or a few layers up can, consider propegating it. Also consider returning a value that simply indicates what the calling code needs to fix.

If your exception cannot be recovered from at all code at some appropriate place in the call hierarchy needs to convert it to a user-friendly error message, ideally alerting the user to a way to fix the exception and re-run the task or even re-start the task at a place where the error occurred.

Finally, whenever you wrap an exception, and there are times that you might want to wrap an exception, wrap the whole exception as the source, so that you have nicely nested exceptions.

If you have a look at the Javadoc for the exception class, you'll see it can take a Throwable. It really depends on what you're doing, but more often than not I choose to preserve the stack trace by doing something like:

catch(SomeException ex) {
    throw new ClearerException("This is what specifically went wrong", ex);
}

That way you get the benefit of a nice message that you can put in, but if you want to delve deeper and find the cause, the stack trace is all there too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM