Very simply:
Before you add a try/catch block to a method because it invokes another method that throws checked exceptions, consider adding a throws clause instead.
I've heard developers complain about checked exceptions in Java because they had to copy and paste empty exception handlers all through their code.
If instead they just "pasted" the throws clause on all method signatures, they would find themselves only doing try/catch blocks at a very high level, such as in event handlers, and in many fewer places. And if they still have empty exception handlers (bad practice, agreed) they can at least reserve the option of taking better action later without having to modify the code everywhere.
I would say it just helps to think before you catch.
-- BillKayser
[Maybe a better way of stating it is in LetExceptionsPropagate. -- Bill Kayser] [See also ConvertExceptions, HomogenizeExceptions, and RefineExceptions]
Some disagree. They think that many try/catch phrases are intended by the writer of the throwing code: he wants you to think about this at the point of usage. Also, propagating exceptions to higher levels often means that the higher level cannot make sense out of specific exceptions any more; it does not know where, when and under what circumstances the exception occurred: the context is lost.
This misses the point. If you have a specific SOLUTION (not crutch, bandaid, workaround, log, or hack), you catch the SPECIFIC exception and SOLVE THE PROBLEM. Otherwise, you are morally and ethically required to pass the exception up the chain of command (possibly wrapping it in a package-specific exception container).
HandlingExceptionsAsEarlyAsPossible is, of course, a rule with exceptions. As noted otherwise, sometimes it makes sense to condense errors into larger families and handle them later.
-- HansRohnert
I disagree with your disagreement, Hans. :) I also think that MOST of the time, a method should just 'throw' everything that happens in other methods it calls. Sure, sometimes exceptions need to be regrouped or recreated, but I'd say this isn't done for a higher level, but rather another horizontal level: When exceptions are thrown between tiers in a 3-tier system, for example. Or, when using a third party library through an adapter. -- RobbShecter
Throwing an exception is not a good way to tell the caller that you expect them to handle an error. That is better done by returning an error code. That's because a return value propagates up exactly one level. Exceptions are specifically designed to propagate error data up an arbitrary number of levels in order to decouple error detection from error handling. The thrower of an exception is specifically relinquishing the right to have any expectations about how the exception will be handled. -- PhilGoodwin
I think, actually, that you are agreeing with my main point which is that the callers of a method should generally be decoupled from the errors that the called method might detect. There are, however, exceptions to this rule, which I also addressed. The clearest example is when the intent of the called method is to detect errors. Often a method will exist only for the purpose of detecting and reporting existing error conditions (as with data validation) or for the purpose of detecting the state of preparedness of the system for making a particular call. In either case it is entirely reasonable to return an error code.
Regarding expectations: when a method's signature indicates that it may return an error code, it is offering a contract to potential callers that stipulates that the caller will handle the error. It is both reasonable and correct to expect callers to abide by this contract. We know from experience, however, that callers will often be so attracted by the services of the method that they will enter into the contract even if they are unable or unwilling to abide by its terms - often with deleterious effects to the program at large. Exceptions offer a way to provide a different, often more attractive, contract to callers. I often provide both kinds of methods in a class as I did in my version of BinarySearchInJava. -- pg
The only reason that ErrorCodes? are likable is because you can check them with a simple IF statement. Instead, let's just add some SyntacticSugar (SyntacticDarkChocolate :D) like so:
if succeeds( x = DangerousFunction?() ): ...Or, for inplace functions ending in '!':
try: something.mutate! except (something): ... # is the same as: if(something.mutate!?): ... else: (error handling)Or only use error codes for functions that fail very commonly, such as StringSearch? returning -1 if it can't find the substring/regular expression.
One of my favorite lines a software guy said to me when I was young was, "Don't detect any errors you don't know what to do with." (The other line I have had very good use of was, "In software, there are only 3 numbers: 0, 1, and N. 0 means you can't have any; 1 means its unique; any other number is a RedHerring because you'll have to change it, so make it changeable from the start.") -- AlistairCockburn
There are actually 4 numbers. The Number 2 is also special: Like the manager said to the engineer who built the first modems: "Why do we need *2* prototypes of this?"
That's still a "one": "One" complete telecommunications system. Hardcoding "one" in this case would mean that you have a modem that can ONLY communicate with one other modem; useful, but not as useful as a modem that works with N other modems.
Very good advice. Another related half-idiom is ConvertExceptions. (Lots of stuff following moved there). -- KyleBrown
When writing objects on which threads synchronize, I have found it useful to always include InterruptedException in the throws clauses of blocking operations. For example, this makes it easy to add timeouts to any blocking operations without complicating the implementation of the synchronization objects themselves: the timeout can be implemented as a separate (reusable) object that interrupts the blocked thread after a delay. -- NatPryce
I was going to add this idiom to Wiki, but was happy to see that someone already added it. I recently used ThrowDontCatch while refactoring some code I wrote some time ago. When I finished refactoring, I was amazed to see how much code simply dropped away. Not only that, but the code became really simple to read. I now encourage clients to consider this idiom before they write thousands of lines of exception handling. -- JoshuaKerievsky
While arguing that you should AvoidExceptionsWheneverPossible, I noticed that this suggestion (can't even begin to call it a pattern) may lead to a profusion of "finally" statements. Thoughts? -- BillTrost
The finally clause is to ensure cleanup (i.e. not just garbage collection) so you mainly use it after allocating some external resource, such as opening a file. It's not the same thing as catching Throwable, i.e. all possible exceptions.
This pattern should only be applied to contexts in which there is not enough information to deal with the likely exceptions. The patterns that should generally be used are ConvertExceptions, HomogenizeExceptions, and RefineExceptions (HandlingExceptionsAsEarlyAsPossible was previously mentioned here, but the page contents were lost). The reasons are thoroughly discussed in the works of BertrandMeyer, i.e., ObjectOrientedSoftwareConstruction. The idea is that the information provided by an exception should be as relevant as possible to the context ultimately responsible for handling it. -- JuancoAnez
I understand the advantages of ThrowDontCatch, but I don't fully understand the interplay between logging and exception handling. If you just declare that you throw the exception, you are not catching it and logging the context in which the exception occurred. Given the above discussion about decoupling, you cannot assume that the exception will ever be caught or logged.
If you do not log the exception when it occurs, it may be too late to understand the context in which it occurred. There are several problems with always catching and logging an exception - every method in the call chain will log the exception and you can't know the severity of the error since you never know if recovery will [?] is possible up the call chain.
-- DavidItkin
I don't think logging implies catching. For example, you can create an exception or throw helper that logs the exception when it is constructed or thrown. This way, you log the exception at the point of origin, not the first place it was caught. -- RobertDiFalco
From a purely pragmatic stand point, I often find it useful (in debugging) to log exceptions where they were thrown and at every catch, log, and rethrow at every calling point. At the point where an exception is thrown, there is usually not enough information to know why something is bad; otherwise you would probably correct it at that point. Getting a NULL pointer exception out of a common routine provides little aid in diagnosing the problem.
Another thought just hit me. Could multiple levels of exception be an argument against extensive refactoring? I'm not sure I accept that, but it probably is worthy of some discussion. -- WayneMack
That's too complicated to me. I treat exceptions the same in both production and debug code. I couldn't imagine putting all those catches just to log and re-throw. I just don't need the information. I just add a debug break to my throw macro and start stepping from there. The only problem is if you wait to break until after the exception is thrown. Then you are screwed and cannot get all that good stack information leading up to the exception. In Java, we have that nice stack trace we can put into an inspector, so I rarely even need the hard break there. I just take a stack at the point of origin and another where it ends up being caught. YMMV. -- RobertDiFalco
Unfortunately, there are many cases where a debugger is not an option. This is particularly true after delivery. I find it useful to have a log file the customer can send back to track down a problem. The only real way I have of getting a "stack dump" is to log the exception stack walk via lots of catches. I have written stack dump utilities in C/assembly to save a stack dump to an error log, and frankly the exception approach is much easier. I don't catch every exception at every possible point (been there, done that, learned better). I still find it help to do in targeted areas. The biggest decision is when, if ever, to clean up the catch blocks. -- WayneMack
So I have this question. Say you call a library function and it can throw an exception, so you put that throws clause into your method signature. Now let's say I call your method and another method (library or other) that throws a different exception, so I add your exception and the other exception to the throws clause of my method, so now I have two exceptions. Now a third method calls my method and another method which can throw two exceptions. So you add all four throws clauses to that method's declaration. Do you see where I'm going here?
aMethod () throws ExceptionA { libraryObject.someMethod(); // can throw ExceptionA } bMethod () throws ExceptionA, ExceptionB () { bMethod(); otherMethod(); // can throw ExceptionB } cMethod () throws ExceptionC, ExceptionD() { eMethod(); // can throw ExceptionD } myMethod () throws ExceptionA, ExceptionB, ExceptionC, ExceptionD { bMethod(); cMethod(); }Now if there is a method that calls myMethod and some other exception-throwing method - eek. The combinatorial explosion could be messy.
OK, you'll argue that perhaps some of these exceptions inherit from a parent exception class, so to catch ExceptionC and ExceptionD I only need to declare throwing ExceptionP? Hm, but what information have you gained or lost? What problem are you solving this way?
-- StevenNewton
In CeePlusPlus, I usually rely on the std::exception::what() function to tell me the information I need to know on the exception type. Of course in the Microsoft world, you end up having to catch std::exception, _com_error, and CException. As a personal standard practice, I always include a text message in the exception and generate the text string on the fly to include specific information on what the bad data was and why it was bad. -- WayneMack
This discussion has two inherent problems that will prevent us from finding the right answers.
(First) ExceptionsInJavaAreDifferentThanInCeePlusPlus
The purpose of exception facilities is to carry all the essential context information from the place a problem occurs to the place that problem can best be resolved. (The qualifier "essential" being somewhat relative to the choice of resolution.) In Java, any context information kindly collected by the thrower by constructing an exception object combined with the stack trace collected by the VM is about all you should ever need. (Additional context may be added as the exception propagates up by catching, adding the extra information to the exception, and rethrowing it.)
(Second) LoggingIsNotHandling (but LoggingIsHandling)
I have used Exceptions to carry error messages, this was for a web based project. It consisted of a command servlet that routes request to action classes which in turn invoke business objects, the business objects interact with one of two databases and a LDAP server. I did not use try catch extensively, the action classes simply threw my custom exception , the business objects used a try catch block when accessing the LDAP server for instance and catch only the LDAPException and wrap it along with the stack trace and any associated exception id into my custom Exception object and throw it, the exception would get propagated all the way up to the servlet which would log it extract the exception id if it can and get a message from a config file for that exception id and display it on the client browser. The config file was so that if a client did not like a particular message it was a simple matter of changing text in a config file with out recompiling. Has anybody used a similar approach or a variant of this approach, potential pitfalls associated with this approach?? -- Vikram
That seems fine to me, so long as the exceptions are written to be independent of the error message selection process; the exceptions should describe the problem, not the solution (or error-reporting) that the application chooses to undertake. It's also a bit iffy if you're throwing the exceptions for the convenience of the error message handling; exceptions should never be things you have around on purpose. For what it's worth, your example seems kosher to me. -- DavisHerring?
A common thing is to HandleExceptionsAtLayerBoundaries?. Say you have layerA and layerB. You have a BusinessException? class which is extended as LayerABusinessException and LayerBBusinessException. If A calls B, typically, the caller of A does not want to know about problems in layer B. So you either propagate the LayerBBusinessException up and WrapExceptionsLate?, or WrapExceptionsEarly? and propagate them up as LayerABusinessException.
See LetExceptionsPropagateOnlyAsUncheckedExceptions