Exceptions are not errors. The distinction is more subtle than most programmers realize.
Things used to be simple in the bad old days when I programmed in C. The rules were simple: don't try to read or write past the end of an array, clean up memory when you are done, check the result of every function to see if it failed and so on. Any violation of these rules, was an error. Using modern languages and libraries things are less cut and dry.
With wonderful new data types in many languages, it is sometimes perfectly okay to try and access the Nth+1 element in array. For instance a Java array is guaranteed to throw an out of bounds exception. This is consistent and reliable behaviour, which does not constitute an error. It might not be what a programmer is intending but on the other hand it is not a violation of the implicit contract of a Java array. Accessing the Nth+1 element in a C++ vector (using the operator ) is undefined behaviour, which means that it is a violation of an implied precondition. It is interesting to note that the vector class provides checked access through the at() member function.
A contract, for those who are unfamiliar, is a set of conditions, implicit or explicit, which must hold true before or after, certain functions. These conditions are called contractual clauses, and there are three principle kinds of contractual clauses:
preconditions are clauses which represent the requirements that must be satisfied before a function is called
postconditions are clauses which represent the obligations that must be satisfied before a function is exited
class invariants are clauses must evaluate to true before entering and before leaving from every public member function
of an object. A class invariant must hold true after an object is constructed, and before it is released
The improtance of the distinction between these two cases becomes clear when we write tests, such as for contract verification. We can't automatically assume that accessing the Nth+1 element of a Java array is an error. It can be done intentionally (i.e. a buffer implementation). Of course doing so can be considered bad style, but it is not a violation of the conditions of use (the contract).
Another problem occurs if we ever want to optimize our array class to remove bounds checking. Doing so would tighten our preconditions for the contract, but it would make any existing code, which depended on the exception checking, incorrect.
Well then clearly we have a dilemma. Most programmers would likely prefer that code never throws an "array out of bounds" exception. Simply labelling such a thing as "bad style" or "poor practice" does little to help us in a real-world scenario. This is too vague of a guideline so as to be easily enforced, nor deeply understood. What I propose is that we must decide in advance whether we want an array which provides checking (i.e. CheckedArray) or does not (i.e. UncheckedArray). By making this distinction in code we are much better off, than by simply crossing our fingers and hoping that everyone in a project understands and follows a vague set of debatable style guidelines.
> Another problem occurs if we ever want to optimize our > array class to remove bounds checking. Doing so would > tighten our preconditions for the contract, but it would > make any existing code, which depended on the exception > checking, incorrect. > > Well then clearly we have a dilemma.
Do we? What is the dilemma in choosing between "obtimized" - but unusable - code and correct code?
Perhaps dilemma is a poor choice of words. I am trying to describe the issue of conflicting goals that can occur when programmers confuse contract violations and exceptional cases, or they specify contracts which are too constrained at what should be a low-level class implementation.