Wednesday, March 30, 2011

But not quite ...

So what went wrong with checked exceptions? Well, they try to force a programmer to think about something that they don't want to, and prevent them from thinking about what they do want to.

So they turn them off, swallowing exceptions left and right. Just letting an exception propagate is much better than swallowing all the time. So what happens is that Checked Exceptions become just as important in an API design as the method name, return type or parameter list as far as the compiler is concerned, but from the point of view of the API designer or the programmer, they are just not very important and tend to be forgotten, causing pain in the API.

Ironically, it is precisely Java where this is a problem. Java is a language of corporate development. Corporate development doesn't generally involve a great amount of thinking about such high level robustness, so a typical Java developer is all the more likely to look at their code and say: "It compiles, it runs, looks good." and not think about error scenarios. It litterally never occurs to them.

Of course not all Java developers, but enough that it matters.

So I think the correct balance in such a language is to have compiler warnings. Let the compiler tag classes that can throw checked exceptions, and if you fail to deal with them explicitly (throws or catch) then it will generate a compiler warning. You either care about that or you don't, but you are not swallowing exceptions.

Wednesday, March 23, 2011

In Defense of Checked Exceptions

I fought in the Checked Exceptions wars, much like your father ...

I have been defending Checked Exceptions since 2004, but lately I have been thinking that it just doesn't fit with where Java is in the computing universe. Ironic since Java is the only language that has them as a compile time constraint (to my knowledge).

Just to reiterate, this is my argument. A followup post will explain why I am walking away from it.

A potentially cogent argument for checked exceptions:

An overly simplistic way of thinking about checked exceptions is that if given the exact same input parameters, the method could sometimes throw an exception, and sometimes not, then that exception should be a checked exception. It tells the user of the API that this is an error condition that they cannot account for ahead of time, prior to calling the method. The programmer writing good code that works needs to realize that his or her application must handle that situation. If by handle that means "crash" then converting to a runtime exception may indeed be appropriate, but that is the clients choice, not the API's.

This is overly simplistic because not all appropriate checked exceptions would fit in this parameter, and there are always grey areas, but this guideline may help develop a reasonable API.

Good examples of such exceptions would be SQLException and ClassNotFoundException. The class being loaded by reflection by definition may not actually be in the classpath at runtime (if you could be sure that it would, you wouldn't need reflection). The database may not be working, or not reachable, at run time.

For operations which always fail given the same input parameters, a runtime exception could well be appropriate (such as ArrayIndexOutOfBounds) but should be documented in the javadocs so that users of the API know what parameters are valid.

One thing which regularly trips up developers, even experienced ones, is that they think they have to handle the "Exception" and not the problem it represents. For example, one of the arguments (expressed here) that checked exceptions are not appropriate is that the caller isn't in a position to deal with the exception.

This is really backwards thinking. When you call a method which relies on a database connection (or other volatile resource such as a file or server connection), you have to know that you may not get what you asked for. Returning null is only appropriate if you could expect to get null legitimately (a value in a column of a database, for example), and the method should be documented to return null. A checked exception means you have to think about what to do about the fact that the method cannot guarantee a consistent result. That is what a checked exception should be telling you: Hey, this may not work out, what do you want to do about that? How do you want to solve the problem when the database went down, or is misconfigured, or whatever?

You should wrap the checked exception when you want to abstract it. For example, if you are writing a datasource API which can flexibly get from a flatfile, a JDBC datasource and/or an XML file, you would want to wrap the various exceptions so that calling programs don't care which one was actually used. But in all cases they are volitile, and may not be available or properly configured at runtime, so the wrapper should be a checked exception.

What to do about exceptions through an interface which cannot throw checked exceptions (Say Comparable, or an ActionListener or the like)? The first thought should be that what the API is telling me is that my operation should be guaranteed. If pressing a button hits the database, then before this method ends, I have to inform the user of failure, and do whatever I wanted to do about that failure, so the event queue can continue on its merry way not caring about the result. Otherwise, the user presses the button, and simply nothing happens. Is that what you want? Comparable is saying: this operation has to work out, with a deterministic result. If it doesn't, it is up to the implementor to figure it out, not the client. It is either greater than, less than, or equal to. There is no option of "I can't figure it out, sometimes." If I always can't figure it out given this input, on the other hand, this is often a ClassCastException, an appropriate use of a runtime exception.

I think this leads to the core problem: Some programmers view checked exceptions as coding problems (how to handle them), not as design considerations (how to construct a program which handles serious potential environment errors in a robust way), and therefore don't handle them properly.

If your answer is that my system assumes that the runtime environment is pristine and never fails, and that level of robustness (or lack thereof) is acceptable to you, then indeed wrap exceptions in a runtime exception. It is a step above swallowing the exception, although probably little better than catching Exception and logging the result. But that is the client's choice, not the API's. The API is telling you something will go wrong which you could not account for programmatically. It is up to you if that bothers you.

In large applications where Exception swallowing becomes the norm, I would bet that that is a symptom of larger problems such as developers producing specific functionality, not well functioning programs, and/or a highly coupled system without clear boundaries of responsibility leaving it unclear when to pass exceptions and when to wrap them, or simply not caring about error handling. Error handling should be as much a requirement as the core functionality. If that requirement is crash early, crash often, then wrapping in runtime exceptions, or even swallowing may be appropriate. At least it is a deliberate decision, and having API's which throw checked exceptions encourage the deliberation, helping good programmers develop good code that works.

Monday, March 14, 2011

Why multiple choice tests are a bad thing

This is a great example of a bad multiple choice test. Multiple choice tests are hard to write because the one writing the question has to anticipate the misunderstandings that can occur. The first two questions are a disaster and the last one isn't any better. Let's take this a question at a time:

1. Sara works full-time at the Big Save store and earns $2,500 per month. Who pays the contributions to Social Security on the $2,500 per month in wages she earns?
A. Only Sara.
B. Only Big Save, her employer.
C. Both Sara and Big Save, her employer.
D. I don’t know.

So the question is "who pays?" I'm sure they are looking for C (even before checking the answers at the bottom), but in fact you could justify every single answer. From an economic perspective, the employer basis the salary it can pay on the added payroll tax and costs, so really Sara is paying for it in a lowered salary - certainly in the aggregate of all the Saras in all the Big Saves of the country. From the perspective of who writes the check, the answer is B. From the perspective of who sees what deduction on their income and or inflation in their payroll expense, that would be C. And of course you could write D just because the question is such a mess.

2. Amanda has $5,000 saved up from working at different jobs. She puts her money in a savings account that pays four percent a year in interest. How much money will be in her account at the end of the first year and at the end of the second year?
A. End of first year: $5,100; end of second year: $5,400.
B. End of first year: $5,200; end of second year: $5,400.
C. End of first year: $5,200; end of second year: $5,408.
D. I don’t know.

Well, this is a compound interest question. They want C. At least A and B are clearly wrong. But the real answer is D - there isn't enough information to know, as you don't know how often the interest compounds. The question assumes annually. I don't know any savings accounts yielding 4% currently, but I have never seen one compound only annually.

5. John drove his car to the local Gas and Shop store. On the way to the store he got distracted while talking to his friend in the car and hit a street sign. Neither he nor his friend was hurt in the accident, but the front end of the car was damaged. What type of automobile insurance coverage will provide reimbursement for damages to his car?
A. liability.
B. collision.
C. comprehensive.
D. I don’t know.

If you get collision *or* comprehensive, you will be covered. Technically when you read down the itemized list of insurance coverage, they may be broken out, but when people think of insurance they often think of levels, so liability is less than collision which is less than comprehensive. So B or C are both financially literate answers, unless you are an insurance agent.