When to consider catching Specific Exceptions Vs General Exception in Java - java

Catching specific exceptions like option 1 below is very common and also considered the save way to catch exceptions, Why the option 2 is rarely considered because of how verbose(not sure if that's the word to use!) it can become, that's what I understand so far.
My question is that are there any other instances when one should consider catching exceptions like option 2(General Exception) rather than Option 1 ?
Opt1 -> Specific Exceptions:
try{
TextIO.putf(s);
FileOutputStream mFileOutputStream = new FileOutputStream("/content/doc/test.pdf", true);
}catch (FileNotFoundException notfEx){
//Do something
}catch (NullPointerException nullEx){
//Do something
}catch(IllegalArgumentException illEx){
//Do something
}
Opt2 -> General Exception:
try{
TextIO.putf(s);
FileOutputStream mFileOutputStream = new FileOutputStream("/content/doc/test.pdf", true);
}catch (Exception ex){
if(ex.getClass() == java.io.FileNotFoundException.class) {
//Do something
}else if(ex.getClass() == java.lang.IllegalArgumentException.class){
//Do something
}else if(ex.getClass() == java.lang.NullPointerException.class){
//Do something
}
}
}

You are simply asking the wrong question, a typical example of pre-mature optimisation.
The (potentially nil) difference in performance between your two options does not matter at all.
Keep in mind: you are talking about dealing with an exception that was just thrown.
Rest assured: creating that exception, and collecting its stack trace is thousands of times more expensive compared to some hypothetical gain because of using your options 1 or 2.
Thus: you can safely ignore performance here. You simply do what is easier to read, and easier to understand for human readers of your code. Which of course dictates to use version 1. And as the comments are pointing out: actually, your option 2 isn't equivalent to option 1 (as it ignores sub classes of exceptions). So, not only is that option 2 way harder to read and maintain, it is also very likely to result in unexpected (wrong) exception handling.
Finally: you are also victim of another common problem with newbie Java programmers. They assume that "what you do in source java" is the relevant factor for java performance at runtime. But that is not true. The real performance of java comes out of the JIT, and its efforts to compile java byte code into machine code. You have to make sure that the JIT can do a good job (and of course: to avoid stupid mistakes within your code). So again: your ideas do not contribute anything meaningful to "how to achieve performance".

Related

Try-catch: is this acceptable practice?

We have received Java code from a software supplier. It contains a lot of try-catch blocks with nothing in the catch part. They're all over the place. Example:
try {
spaceBlock.enable(LindsayModel);
} catch (Exception e) {
}
My questions are: Is the above acceptable practice? If so, when? Or should I just go ahead and remove all of these "bogus" try and catch statements?
To me this looks like terrible practice, but I'm not experienced enough in Java to tell for sure. Why catch errors if you're not going to do anything with them? Seems to me, you would only do that if you were confident that an exception would be of absolutely no consequence and you don't care if one occurs. However, this is not really the case in our particular application.
EDIT To give some context: We bought a Java-scriptable product from the supplier. Alongside the product, they provided a large proof-of-concept script tailored to our needs. This script came "free of charge" (though we wouldn't have bought the product if it hadn't come with the script) and it "works". But the script is a real pain to build upon, due to many things that even I as a Java novice recognise as awful practice, one instance being this bogus try-catch business.
This is indeed terrible practice. Especially the catching of Exception rather than something specific gives off a horrible smell - even a NullPointerException will be swallowed. Even if it is assured that a particular thrown exception is of no real consequence, one should always log it at the very least:
try {
// code
}
catch (MyInconsequentialException mie) {
// tune level for this logger in logging config file if this is too spammy
MY_LOGGER.warning("Caught an inconsequential exception.", mie);
}
However it is unlikely an exception is completely meaningless in this situation. I recommend researching exactly what exception(s) the application's code is intending to swallow here, and what they would really mean for the execution.
One important distinction is whether the try/catches are used to swallow checked exceptions. If this is the case, it probably indicates extreme apathy on the programmer's part - somebody just wanted his/her code to compile. At the least, the code should be amended:
try {
// code
}
catch (SpecificCheckedException sce) {
// make sure there is exception logging done farther up
throw new RuntimeException(sce);
}
This will rethrow the exception wrapped in an unchecked RuntimeException, effectively allowing the code to compile. Even this can be considered a bandaid however - best practice for checked exceptions is to handle them on an individual basis, either in the current method or farther up by adding throws SpecificCheckedException to the method signature.
As #Tom Hawtin mentioned, new Error(sce) can be used instead of new RuntimeException(sce) in order to circumvent any additional Exception catches farther up, which makes sense for something that isn't expected to be thrown.
If the try/catch is not being used to swallow checked exceptions, it is equally dangerous and should simply be removed.
Terrible, indeed. Swallowing an exception like this can be dangerous. How will you know if something bad has happened?
I'd feel better if the vendor wrote comments to document and acknowledge it ("We know what we're doing"). I'd feel even better if there was a strategy apparent in the code to deal with the consequences. Wrap it in a RuntimeException and rethrow; set the return value to an appropriate value. Anything!
"All over the place"? Are there multiple try/catch blocks littering the code? Personally, I don't like that idiom. I prefer one per method.
Maybe you should find a new vendor or write your own.
try {
meshContinuum.enable(MeshingModel);
} catch (Exception e) {
}
This looks like unfinished code. If the enable method throws an Exception then it will be caught and swallowed by the code. If it doesn't then it does not make sense to try to catch a non occuring exception.
Check to see the methods and where their signatures are not followed by throws exceptionName, then remove the empty try-catch statements from the places they are called.
You can theoretically put try-catch around any statement. The compiler will not complain about it. It does not make sense though, since this way one may hide real exceptions.
You can see this as a sign of bad code quality. You should probably be prepared to run into problems of different type too.
It's not the best:
It hides evidence of the exception so debugging is harder
It may cause features to fail silently
It suggests that the author might actually have wanted to handle the exception but never got around to it
So, there may be cases where this is OK, such as an exception that really is of no consequence (the case that comes to mind is Python's mkdirs, which throws an exception if the directory already exists), but usually, it's not so great.
Unfortunately you cannot just remove it, because it the try block throws a checked exception then it will have to be declared in the throws clause of the method. The callers of the method will then have to catch it (but not if the callers also have this catch (Exception e) {} abomination).
As an alternative, consider replacing it with
try {
meshContinuum.enable(MeshingModel);
} catch (Exception e) {
throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);
}
Since RuntimeException (and classes that extend it) are unchecked exceptions they do not need to be declared in the throws clause.
What did your contract with the supplier specify? If what they wrote, bad practice and all, meets the spec then they will charge you for a rewrite.
Better to specify a set of tests that will enter many or all of those try-catch blocks and hence fail. If the tests fail you have a better argument to make them fix their terrible code.
Horrible idea on the face of it, totally depends on what you're actually calling. Usually it's done out of laziness or habituated bad practices.
Actually ... not so fast.
There are legitimate cases for ignoring an exception.
Suppose that an exception has happened already, and we're already in a catch(). While in the catch(), we want to make best effort to clean up (which could fail, too). Which exception to return?? The original one, of course. The code looks like this:
try {
something-that-throws();
} catch(Exception e) {
try {
something-else-that-throws();
} catch(Exception e1) {}
throw e;
}
When we really don't care whether an operation succeeds, but (unfortunately) the operation's author throws an exception if a failure occurs.
if (reinitialize) {
try {
FileUtils.forceDelete(sandboxFile); // delete directory if it's there
} catch(Exception e) {}
}
The above saves a rather tortured attempt to see if sandboxFile actually exists before deleting it anyway.

Should java try blocks be scoped as tightly as possible?

I've been told that there is some overhead in using the Java try-catch mechanism. So, while it is necessary to put methods that throw checked exception within a try block to handle the possible exception, it is good practice performance-wise to limit the size of the try block to contain only those operations that could throw exceptions.
I'm not so sure that this is a sensible conclusion.
Consider the two implementations below of a function that processes a specified text file.
Even if it is true that the first one incurs some unnecessary overhead, I find it much easier to follow. It is less clear where exactly the exceptions come from just from looking at statements, but the comments clearly show which statements are responsible.
The second one is much longer and complicated than the first. In particular, the nice line-reading idiom of the first has to be mangled to fit the readLine call into a try block.
What is the best practice for handling exceptions in a funcion where multiple exceptions could be thrown in its definition?
This one contains all the processing code within the try block:
void processFile(File f)
{
try
{
// construction of FileReader can throw FileNotFoundException
BufferedReader in = new BufferedReader(new FileReader(f));
// call of readLine can throw IOException
String line;
while ((line = in.readLine()) != null)
{
process(line);
}
}
catch (FileNotFoundException ex)
{
handle(ex);
}
catch (IOException ex)
{
handle(ex);
}
}
This one contains only the methods that throw exceptions within try blocks:
void processFile(File f)
{
FileReader reader;
try
{
reader = new FileReader(f);
}
catch (FileNotFoundException ex)
{
handle(ex);
return;
}
BufferedReader in = new BufferedReader(reader);
String line;
while (true)
{
try
{
line = in.readLine();
}
catch (IOException ex)
{
handle(ex);
break;
}
if (line == null)
{
break;
}
process(line);
}
}
The basic premise here is false: the size of a try block makes no difference in performance. Performance is affected by actually raising exceptions at runtime, and that's independent of the size of the try block.
However, keeping try blocks small can lead to better programs.
You might catch exceptions to recover and proceed, or you might catch them simply to report them to the caller (or to a human, via some UI).
In the first case, failures from which you can recover are often very specific, and this leads to smaller try blocks.
In the second case, where an exception is caught so that it can be wrapped by another exception and re-thrown, or displayed to the user, small try blocks mean that you know more precisely which operation failed, and the higher-level context in which that call was made. This allows you to create more specific error reports.
Of course, there are… exceptions (sorry!) to these guidelines. For example, in some cases very specific error reports could be a security problem.
It might be useful to know what effect a try block has on the compiled code. It doesn't change the compiled instructions at all! (Of course, the corresponding catch block does, since it's like any other code.)
A try block creates an entry in the exception table associated with the method. This table has a range of source instructions counters, an exception type, and a destination instruction. When an exception is raised, this table is examined to see if there is an entry with a matching type, and a range that includes the instruction that raised the exception. If it does, execution branches to the corresponding destination number.
The important thing to realize is that this table isn't consulted (and has no effect on running performance) unless it's needed. (Neglecting a little overhead in the loading of the class.)
I've been told that there is some overhead in using the Java try-catch mechanism.
Absolutely. And there's overhead to method calls, too. But you shouldn't put all your code in one method.
Not to toot the premature optimization horn, but the focus should be on ease of reading, organization, etc. Language constructs rarely impact performance as much as system organization and choice of algorithms.
To me, the first is easiest to read.
No. The only thing that you should be considering is where you can reasonably handle the exception and what resources you need to reclaim (with finally).
This is premature optimization at its worst. Don't do it.
"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil" - Knuth.
there is very very little benefit to the 2nd method. after all if you can successfully open a file but not read from it, then there is something very wrong with your computer. thus knowing that the io exception came from the readLine() method is very rarely useful. also as you know, different exceptions are thrown for different problems anyway (FileNotFoundException, etc)
as long as you scope it with a 'logical' block, ie opening, reading, and closing a file in 1 go, i would go with the first method. it's much simpler to read and, especially when dealing with IO, the processor cycles used by the try-catch overhead would be minimal if any.
Putting the try blocks around the specific code that may throw an exception, makes it, in my opinion easier to read. You're likely to want to display a different message for each error and provide instructions to the user, which will be different depending on where the error occurs.
However, the performance issue that most people refer to is related to raising the exception, not to the try block itself.
In other words, as long as you never have an error raised, the try block won't noticeably affect performance. You shouldn't consider a try block just another flow control construct and raise an error to branch through your code. That's what you want to avoid.
The second method will generate a compiler error that reader may not have been initialized. You can get around that by initializing it to null, but that just means you could get an NPE, and there's no advantage to that.

Is there a limit on the number of exceptions I should throw from a method?

Will declaring the function in this way have any implications on the performance?
public init(){
try{
initApplication();
}catch(A1Exception){
}catch(A2Exception){
...
}catch(A5Exception){
}
}
private void initApplication() throws A1Exception, A2Exception, A3Exception, A4Exception, A5Exception {
initApp1(); //throws A1, A2, A3
initApp2(); //throws A4, A5
}
Are there any issues with implementing initApplication() in this way?
In recent years there's been a feeling that checked exceptions are fairly harmful. Each of these exceptions, if they are checked, forces the calling methods to have to handle them or declare them. This breaks encapsulation, because now something of the lower level implementation details leaks up into the higher levels.
Joshus Bloch talks about this in Effective Java, which I highly recommend.
There is no limitation as to how many exceptions can be thrown by a method.
The more exception you throw, the more you can be specific about any exceptions being caught.
Just I would like to point out a few suggestions which I follow.
1) Atleast have a generic exception at the last so if any other exception which may occur in your code is caught than being thrown to the calling class.
2) You can have category of Exception Classes like BusinessLogic Exception, InvalidDataException, SystemsException so you may have actually less no of exceptions being thrown from any method. (Unless your business demand exact exception
3) Always have error codes than throwing actual text messages which will make your application language independant.
I don't see a problem throwing whatever Exception your code requires. My first impression when looking at the example is that Exceptions might be used here to control the flow of your application. Be careful not to do that. Exceptions should only be triggered in exceptional cases.
One reason why process flow should not be handled via Exceptions is that raising Exceptions is an expensive process. Though the structure of multiple catch blocks shouldnt result in a performance hit, the (potential) underlying process that uses Exceptions to control flow would not perform well.
With that in mind, is there a 'smell'? Only if the above concern is true in the design of the code.
Q1. Will this have performance impact?
No, not if they are 'genuine' exceptions (i.e. not being used for normal program control flow)
Q2. Do you see smell in initApplication()?
No, not if initApplication() can be expected to raise N exceptions, i.e. if there are N - exceptional circumstances that could prevent initApplication() from completing it's work.
1: The performance impact will be minimal...
2: But it's not necessarily a good idea, as having excessive boiler plate code makes it hard to read. If every exception is going to be handled the same way, then you should either do a catch of the base class Exception. Or you can have initApplication() throw a custom exception -- something like ApplicationInitializationException, which conveys a little more meaning about what went wrong. You can set the message for the exception to be the exact details.
There are some cases where you might get 5 different exceptions, and might have to deal with all of them differently. In that case, catching all 5 would be the proper thing to do. But it's worth thinking about if that's really necessary before you implement it.
If there is no exception there is no performance impact, if an exception is thrown, there is some overhead finding the matching catch which will grow as you add more exceptions, but it should not matter, because exceptions should be er.. exceptional. The should not happen under ordinary circumstances.
In other words if you do not use throwing exceptions to control the flow of your program yuo should be ok, and if you do this is certainly a smell no matter how many catch clauses you have
On a purely technical note, the class file format introduces an upper limit of 65536 exceptions. The u2 type of exceptions_table_length is shorthand for an unsigned two byte quantity.
The Code attribute has the following format:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
I only bring this up in case you are generating code. JSP developers often have to worry about problems with generated methods exceeding 64KB code size. This and other limitations are listed in Section 4.10 Limitations of the Java Virtual Machine.
I guess this answer is mostly related to the Q2 but view of it that is not so technical but might be worth considering anyway are the one behind the following two questions?
Does it make sense for the user to catch all of them?
Do you want to risk the user getting fed up with having lots of catch blocks and instead just insert a single "catch(Exception ex) {// ex.printStackTrace(); // enable this if we see problems}" or something similar and stupid.?
In this case I guess it might not be relevant but I think it is worth considering when doing API design in general.
Do not throw any exceptions unless you are going to take an action if an exception is caught. Convert your checked exceptions to runtime exceptions for logging purposes.
More info on the topic can be found here

How expensive are Exceptions [duplicate]

This question already has answers here:
What are the effects of exceptions on performance in Java?
(18 answers)
Closed 9 years ago.
Do you know how expensive exception throwing and handling in java is?
We had several discussions about the real cost of exceptions in our team. Some avoid them as often as possible, some say the loss of performance by using exceptions is overrated.
Today I found the following piece of code in our software:
private void doSomething()
{
try
{
doSomethingElse();
}
catch(DidNotWorkException e)
{
log("A Message");
}
goOn();
}
private void doSomethingElse()
{
if(isSoAndSo())
{
throw new DidNotWorkException();
}
goOnAgain();
}
How is the performance of this compared to
private void doSomething()
{
doSomethingElse();
goOn();
}
private void doSomethingElse()
{
if(isSoAndSo())
{
log("A Message");
return;
}
goOnAgain();
}
I don't want to discuss code aesthetic or anything, it's just about runtime behaviour!
Do you have real experiences/measurements?
Exceptions are not free... so they are expensive :-)
The book Effective Java covers this in good detail.
Item 39 Use exceptions only for exceptional conditions.
Item 40 Use exceptions for recoverable conditions
The author found that exceptions resulted in the code tunning 70 times slower for his test case on his machine with his particular VM and OS combo.
The slowest part of throwing an exception is filling in the stack trace.
If you pre-create your exception and re-use it, the JIT may optimize it down to "a machine level goto."
All that having been said, unless the code from your question is in a really tight loop, the difference will be negligible.
The slow part about exceptions is building the stack trace (in the constructor of java.lang.Throwable), which depends on stack depth. Throwing in itself is not slow.
Use exceptions to signal failures. The performance impact then is negligible and the stack trace helps to pin-point the failure's cause.
If you need exceptions for control flow (not recommended), and profiling shows that exceptions are the bottleneck, then create an Exception subclass that overrides fillInStackTrace() with an empty implementation. Alternatively (or additionally) instantiate only one exception, store it in a field and always throw the same instance.
The following demonstrates exceptions without stack traces by adding one simple method to the micro benchmark (albeit flawed) in the accepted answer:
public class DidNotWorkException extends Exception {
public Throwable fillInStackTrace() {
return this;
}
}
Running it using the JVM in -server mode (version 1.6.0_24 on Windows 7) results in:
Exception:99ms
Boolean:12ms
Exception:92ms
Boolean:11ms
The difference is small enough to be ignorable in practice.
I haven't bothered to read up on Exceptions but doing a very quick test with some modified code of yours I come to the conclusion that the Exception circumstance quite a lot slower than the boolean case.
I got the following results:
Exception:20891ms
Boolean:62ms
From this code:
public class Test {
public static void main(String args[]) {
Test t = new Test();
t.testException();
t.testBoolean();
}
public void testException() {
long start = System.currentTimeMillis();
for(long i = 0; i <= 10000000L; ++i)
doSomethingException();
System.out.println("Exception:" + (System.currentTimeMillis()-start) + "ms");
}
public void testBoolean() {
long start = System.currentTimeMillis();
for(long i = 0; i <= 10000000L; ++i)
doSomething();
System.out.println("Boolean:" + (System.currentTimeMillis()-start) + "ms");
}
private void doSomethingException() {
try {
doSomethingElseException();
} catch(DidNotWorkException e) {
//Msg
}
}
private void doSomethingElseException() throws DidNotWorkException {
if(!isSoAndSo()) {
throw new DidNotWorkException();
}
}
private void doSomething() {
if(!doSomethingElse())
;//Msg
}
private boolean doSomethingElse() {
if(!isSoAndSo())
return false;
return true;
}
private boolean isSoAndSo() { return false; }
public class DidNotWorkException extends Exception {}
}
I foolishly didn't read my code well enough and previously had a bug in it (how embarassing), if someone could triple check this code I'd very much appriciate it, just in case I'm going senile.
My specification is:
Compiled and run on 1.5.0_16
Sun JVM
WinXP SP3
Intel Centrino Duo T7200 (2.00Ghz, 977Mhz)
2.00 GB Ram
In my opinion you should notice that the non-exception methods don't give the log error in doSomethingElse but instead return a boolean so that the calling code can deal with a failure. If there are multiple areas in which this can fail then logging an error inside or throwing an Exception might be needed.
This is inherently JVM specific, so you should not blindly trust whatever advice is given, but actually measure in your situation. It shouldn't be hard to create a "throw a million Exceptions and print out the difference of System.currentTimeMillis" to get a rough idea.
For the code snippet you list, I would personally require the original author to thoroughly document why he used exception throwing here as it is not the "path of least surprises" which is crucial to maintaining it later.
(Whenever you do something in a convoluted way you cause unneccesary work to be done by the reader in order to understand why you did it like that instead of just the usual way - that work must be justified in my opinion by the author carefully explaining why it was done like that as there MUST be a reason).
Exceptions are a very, very useful tool, but should only be used when necessary :)
I have no real measurements, but throwing an exception is more expensive.
Ok, this is a link regarding the .NET framework, but I think the same applies to Java as well:
exceptions & performance
That said, you should not hesitate to use them when appropriate. That is : do not use them for flow-control, but use them when something exceptional happend; something that you didn't expect to happen.
I think if we stick to using exceptions where they are needed (exceptional conditions), the benefits far outweigh any performance penalty you might be paying. I say might since the cost is really a function of the frequency with which exceptions are thrown in the running application.
In the example you give, it looks like the failure is not unexpected or catastrophic, so the method should really be returning a bool to signal its success status rather than using exceptions, thus making them part of regular control flow.
In the few performace improvement works that I have been involved in, cost of exceptions has been fairly low. You would be spending far more time time in improving the complexity of common, hightly repeating operations.
Thank you for all the responses.
I finally followed Thorbjørn's suggestion and wrote a little test programm, measuring the performance myself. The result is: No difference between the two variants (in matters of performance).
Even though I didn't ask about code aesthetics or something, i.e. what the intention of exceptions was etc. most of you addressed also that topic. But in reality things are not always that clear... In the case under consideration the code was born a long time ago when the situation in which the exception is thrown seemed to be an exceptional one. Today the library is used differently, behaviour and usage of the different applications changed, test coverage is not very well, but the code still does it's job, just a little bit too slow (That's why I asked for performance!!). In that situation, I think, there should be a good reason for changing from A to B, which, in my opinion, can't be "That's not what exceptions were made for!".
It turned out that the logging ("A message") is (compared to everything else happening) very expensive, so I think, I'll get rid of this.
EDIT:
The test code is exactly like the one in the original post, called by a method testPerfomance() in a loop which is surrounded by System.currentTimeMillis()-calls to get the execution time...but:
I reviewed the test code now, turned of everything else (the log statement) and looping a 100 times more, than before and it turns out that you save 4.7 sec for a million calls when using B instead of A from the original post. As Ron said fillStackTrace is the most expensive part (+1 for that) and you can save nearly the same (4.5 sec) if you overwrite it (in the case you don't need it, like me). All in all it's still a nearly-zero-difference in my case, since the code is called 1000 times an hour and the measurements show I can save 4.5 millis in that time...
So, my 1st answer part above was a little misleading, but what I said about balancing the cost-benefit of a refactoring remains true.
I think you're asking this from slightly the wrong angle. Exceptions are designed to be used to signal exceptional cases, and as a program flow mechanism for those cases. So the question you should be asking is, does the "logic" of the code call for exceptions.
Exceptions are generally designed to perform well enough in the use for which they are intended. If they're used in such a way that they're a bottleneck, then above all, that's probably an indication that they're just being used for "the wrong thing" full stop-- i.e. what you have underlyingly is a program design problem rather than a performance problem.
Conversely, if the exception appears to be being "used for the right thing", then that probably means it'll also perform OK.
Let's say exception won't occur when trying to execute statements 1 and 2. Are there ANY performance hits between those two sample-codes?
If no, what if the DoSomething() method has to do a huuuge amount of work (loads of calls to other methods, etc.)?
1:
try
{
DoSomething();
}
catch (...)
{
...
}
2:
DoSomething();

Java exception handling idioms ... who's right and how to handle it?

I currently have a technical point of difference with an acquaintance. In a nutshell, it's the difference between these two basic styles of Java exception handling:
Option 1 (mine):
try {
...
} catch (OneKindOfException) {
...
} catch (AnotherKind) {
...
} catch (AThirdKind) {
...
}
Option 2 (his):
try {
...
} catch (AppException e) {
switch(e.getCode()) {
case Constants.ONE_KIND:
...
break;
case Constants.ANOTHER_KIND:
...
break;
case Constants.A_THIRD_KIND:
...
break;
default:
...
}
}
His argument -- after I used copious links about user input validation, exception handling, assertions and contracts, etc. to back up my point of view -- boiled down to this:
"It’s a good model. I've used it since me and a friend of mine came up with it in 1998, almost 10 years ago. Take another look and you'll see that the compromises we made to the academic arguments make a lot of sense."
Does anyone have a knock-down argument for why Option 1 is the way to go?
When you have a switch statement, you're less object oriented. There are also more opportunities for mistakes, forgetting a "break;" statement, forgetting to add a case for an Exception if you add a new Exception that is thrown.
I also find your way of doing it to be MUCH more readable, and it's the standard idiom that all developers will immediately understand.
For my taste, the amount of boiler plate to do your acquaintance's method, the amount of code that has nothing to do with actually handling the Exceptions, is unacceptable. The more boilerplate code there is around your actual program logic, the harder the code is to read and to maintain. And using an uncommon idiom makes code more difficult to understand.
But the deal breaker, as I said above, is that when you modify the called method so that it throws an additional Exception, you will automatically know you have to modify your code because it will fail to compile. However, if you use your acquaintance's method and you modify the called method to throw a new variety of AppException, your code will not know there is anything different about this new variety and your code may silently fail by going down an inappropriate error-handling leg. This is assuming that you actually remembered to put in a default so at least it's handled and not silently ignored.
the way option 2 is coded, any unexpected exception type will be swallowed! (this can be fixed by re-throwing in the default case, but that is arguably an ugly thing to do - much better/more efficient to not catch it in the first place)
option 2 is a manual recreation of what option 1 most likely does under the hood, i.e. it ignores the preferred syntax of the language to use older constructs best avoided for maintenance and readability reasons. In other words, option 2 is reinventing the wheel using uglier syntax than that provided by the language constructs.
clearly, both ways work; option 2 is merely obsoleted by the more modern syntax supported by option 1
I don't know if I have a knock down argument but initial thoughts are
Option 2 works until your trying to catch an Exception that doesn't implement getCode()
Option 2 encourages the developer to catch general exceptions, this is a problem because if you don't implement a case statement for a given subclass of AppException the compiler will not warn you. Ofcourse you could run into the same problem with option 1 but atleast option 1 does not activly encourage this.
With option 1, the caller has the option of selecting exactly which exception to catch, and to ignore all others. With option 2, the caller has to remember to re-throw any exceptions not explicitly caught.
Additionally, there's better self-documentation with option 1, as the method signature needs to specify exactly which exceptions are thrown, rather than a single over-riding exception.
If there's a need to have an all-encompassing AppException, the other exception types can always inherit from it.
The knock-down argument would be that it breaks encapsulation since I now I have to know something about the subclass of Exception's public interface in order to handle exceptions by it. A good example of this "mistake" in the JDK is java.sql.SQLException, exposing getErrorCode and getSQLState methods.
It looks to me like you're overusing exceptions in either case. As a general rule, I try to throw exceptions only when both of the following are true:
An unexpected condition has occurred that cannot be handled here.
Somebody will care about the stack trace.
How about a third way? You could use an enum for the type of error and simply return it as part of the method's type. For this, you would use, for example, Option<A> or Either<A, B>.
For example, you would have:
enum Error { ONE_ERROR, ANOTHER_ERROR, THIRD_ERROR };
and instead of
public Foo mightError(Bar b) throws OneException
you will have
public Either<Error, Foo> mightError(Bar b)
Throw/catch is a bit like goto/comefrom. Easy to abuse. See Go To Statement Considered Harmful. and Lazy Error Handling
I think it depends on the extent to which this is used. I certainly wouldn't have "one exception to rule them all" which is thrown by everything. On the other hand, if there is a whole class of situations which are almost certainly going to be handled the same way, but you may need to distinguish between them for (say) user feedback purposes, option 2 would make sense just for those exceptions. They should be very narrow in scope - so that wherever it makes sense for one "code" to be thrown, it should probably make sense for all the others to be thrown too.
The crucial test for me would be: "would it ever make sense to catch an AppException with one code, but want to let another code remain uncaught?" If so, they should be different types.
Each checked Exception is an, um, exception condition that must be handled for the program behavior to be defined. There's no need to go into contractual obligations and whatnot, it's a simple matter of meaningfulness. Say you ask a shopkeeper how much something costs and it turns out the item is not for sale. Now, if you insist you'll only accept non-negative numerical values for an answer, there is no correct answer that could ever be provided to you. This is the point with checked exceptions, you ask that some action be performed (perhaps producing a response), and if your request cannot be performed in a meaningful manner, you'll have to plan for that reasonably. This is the cost of writing robust code.
With Option 2 you are completely obscuring the meaning of the exception conditions in your code. You should not collapse different error conditions into a single generic AppException unless they will never need to be handled differently. The fact that you're branching on getCode() indicates otherwise, so use different Exceptions for different exceptions.
The only real merit I can see with Option 2 is for cleanly handling different exceptions with the same code block. This nice blog post talks about this problem with Java. Still, this is a style vs. correctness issue, and correctness wins.
I'd support option 2 if it was:
default:
throw e;
It's a bit uglier syntax, but the ability to execute the same code for multiple exceptions (ie cases in a row) is much better. The only thing that would bug me is producing a unique id when making an exception, and the system could definitely be improved.
Unnecessary have to know the code and declare constants for the exception which could have been abstract when using option 1.
The second option (as I guess) will change to traditional (as option 1) when there is only one specific exception to catch, so I see inconsistencey over there.
Use both.
The first for most of the exceptions in your code.
The second for those very "specific" exceptions you've create.
Don't struggle with little things like this.
BTW 1st is better.

Categories

Resources