简体   繁体   中英

Throw a long list of exceptions vs throw an Exception vs throw custom exception?

I have an application which uses two methods of an API. Both these methods throw more than five exceptions each. So, if I just add a throws declaration then it becomes a list of more than ten. ( My method cannot handle any of the ten exceptions )

I have read that throwing a long list of exceptions is a bad practice. Also throwing (the umbrella) Exception is a bad practice. So, what should I do?

  1. Add a try catch block, and log and exit in the catch block?
  2. Create a custom exception class, wrap every exception and throw the custom exception?
  3. Add a throws declaration for all exceptions?
  4. Throw Exception?
  5. Add a try catch block, and throw a RuntimeException in the catch block? (Current approach)

Edit: Added choice 5.

"2. Create a custom exception class", but not for all. Wrap the exceptions in logical groups. For example you can have XmlWritingException , PaymentGatewayException and DataAccessException which wrap different exceptions depending on the scenario.

It's even possible (and preferred) to wrap the same exception in different wrapper. For example you can wrap IOException in PaymentGatewayException if payment has failed due to communication problem, but in XmlWritingException if it has failed during some i/o operation with xml. All these examples are hypothetical, but you get the idea.

Most importantly - set the original exception as a cause of the new one, so that it does not get lost.

Update: In fact option 5 is fine in case you can't expect the client to reasonably recover from the exception. Even better - the custom exceptions you create can extend RuntimeException . That's what spring does, for example, wrapping all data-related exceptions in DataAccessException .

If callers of your code could conceivably handle some of the exceptions, then I would declare your method as throwing them and pass them through. If there's no hope of that, then I would create your own custom exception that extends (unchecked) RuntimeException, and throw your custom exception with the actual exception chained into it.

I would generally defer decisions to exit to a policy at as high point as possible. Exiting (if you mean exiting the process) should always be deferrable in the code so you can unit test it.

In general, my philosophy around exceptions is:

  1. If the error is a "business logic problem" and the caller can handle it (by choosing alternate logic or by asking the user what to do), then throw a custom checked exception. Example: Throw UserDoesNotExistException to indicate that a user name does not exist in the data system and let the next layer up ask the user to create an account. While you're at it, you might want to think about whether the code could be structured in a way such that the exception is not actually needed.
  2. Exceptions that are unexpected should be runtime (unchecked) exceptions. You want them to throw through your code largely unchanged to as high a point as possible (thread stack is small) where they can be handled as exceptional conditions (by throwing an error page, logging the error, sending an alert to an operator, introspecting the state of the system and including that with the error report, etc). Do not catch, wrap, and rethrow UNLESS you can add actual information that can be used to diagnose the problem.

Generally speaking, if your method cannot handle exceptions, it should re-throw them or add them to throws section. Hiding exceptions in catch block seems to be the worst choice.

As always, it depends.

  1. Is it your API? If it is, then change it an let it throws less exceptions. Handle them in some way.

  2. Try to make your program work as long as possible. That means, that if there's no configuration file, don't throw IOException and exit. Hardcode some default configuration. If there's no internet connection, show the message and don't exit.

  3. Sometimes, it's possible to return something special (variations on Special Case pattern and Null Object pattern) rather then throw an exception.

  4. Consider implementing Observer pattern for some exceptional cases. Don't throw an exception, but notify someone who should show message or do something else.

  5. Don't hide any of them. Log all exceptions.

If your API is called Foo, I would create an exception called FooAPIException. Make sure to embedded the source exception within FooAPIException. When logging and displaying FooAPIException also display the stacktrace of the source exception.

For example:

public class FooAPIException extends Exception {
    private Exception root;

    public FooAPIException (Exception e) {
        super(e.getMessage(),e.getErrorCode());
        root = e;
    }

    public Exception getRoot () {
        return root;
    }

// Exception ================================================================
    public String toString () {
        StringBuffer sb = new StringBuffer();

        sb.append("[");
        sb.append(getErrorCode());
        sb.append("][");
        sb.append(getMessage());
        sb.append("]\n");
        sb.append("ROOT CAUSE:");

        Writer write = new StringWriter();
        PrintWriter pw = new PrintWriter(write);
        e.printStackTrace(pw);
        pw.close();
        try {
            write.close();
        } catch (IOException ioe) {/**/}
        root = write.toString();
        sb.append(write);

        return sb.toString();
    }   
}

And then wrap the API:

public class FooAPI {
    public static method (String params) throws FooAPIException {
        try {
            RealFooAPI.method(params)
        } catch (Exception e) {
            throw new FooAPIException(e);
        }
    }
}

You could wrap the exceptions with some custom exception.

The problem with this is however, that by hiding what the cause exception was (say, for example, it was IOException ), you hide the information that would theoretically allow a higher level of code to catch that exception specifically and deal with it. In that case you are no better off than throwing a RuntimeException , so you may as well wrap them with RuntimeException .

I would still leave the ones that you could imagine a reasonable caller wishing to catch specifically and declare them specifically, or wrap them in groups.

If you are not handle the exception in that method, it is better to wrap them into one or more custom exceptions and throw them so you can handle them in an upper level.

You may also create a custom unchecked (extending RuntimeException) exception and wrap thrown exceptions into your custom runtime exception.

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