> ### Section 9.4
> Programming with Exceptions

> EXCEPTIONS CAN BE USED to help write robust programs. They provide an organized and structured approach to robustness. Without exceptions, a program can become cluttered with `if` statements that test for various possible error conditions. With exceptions, it becomes possible to write a clean implementation of an algorithm that will handle all the normal cases. The exceptional cases can be handled elsewhere, in a `catch` clause of a `try` statement.

> ### Writing New Exception Classes

> When a program encounters an exceptional condition and has no way of handling it immediately, the program can throw an exception. In some cases, it makes sense to throw an exception belonging to one of Java's predefined classes, such as `IllegalArgumentException` or `IOException`. However, if there is no standard class that adequately represents the exceptional condition, the programmer can define a new exception class. The new class must extend the standard class `Throwable` or one of its subclasses. In general, the new class will extend `RuntimeException` (or one of its subclasses) if the programmer does `not` want to require mandatory exception handling. To create a new exception class that does require mandatory handling, the programmer can extend one of the other subclasses of `Exception` or can extend `Exception` itself.

> Here, for example, is a class that extends `Exception`, and therefore requires mandatory exception handling when it is used:

>            public class ParseError extends Exception {
>               public ParseError(String message) {
>                     // Constructor.  Create a ParseError object containing
>                     // the given message as its error message.
>                  super(message);
>               }
>            }

> The class contains only a constructor that makes it possible to create a `ParseError` object containing a given error message. (The statement "`super(message)`" calls a constructor in the superclass, `Exception`. See Section 5.5.) Of course the class inherits the `getMessage()` and `printStackTrace()` routines from its superclass. If `e` refers to an object of type `ParseError`, then the function call `e.getMessage()` will retrieve the error message that was specified in the constructor. But the main point of the `ParseError` class is simply to exist. When an object of type `ParseError` is thrown, it indicates that a certain type of error has occurred. (Parsing, by the way, refers to figuring out the meaning of a string. A `ParseError` would indicate, presumably, that some string being processed by the program does not have the expected form.)

> A `throw` statement can be used in a program to throw an error of type `ParseError`. The constructor for the `ParseError` object must specify an error message. For example:

>            throw new ParseError("Encountered an illegal negative number.");

> or

>            throw new ParseError("The word '" + word
>                                           + "' is not a valid file name.");

> If the `throw` statement does not occur in a `try` statement that catches the error, then the subroutine that contains the `throw` statement must declare that it can throw a `ParseError`. It does this by adding the clause "`throws ParseError`" to the subroutine heading. For example,

>              void getUserData() throws ParseError {
>                 . . .
>              }

> This would not be required if `ParseError` were defined as a subclass of `RuntimeException` instead of `Exception`, since in that case exception handling for `ParseErrors` would not be mandatory.

> A routine that wants to handle `ParseErrors` can use a `try` statement with a `catch` clause that catches `ParseErrors`. For example:

>              try {
>                 getUserData();
>                 processUserData();
>              }
>              catch (ParseError pe) {
>                 . . .  // Handle the error
>              }

> Note that since `ParseError` is a subclass of `Exception`, a `catch` clause of the form "`catch (Exception e)`" would also catch `ParseErrors`, along with any other object of type `Exception`.

> Sometimes, it's useful to store extra data in an exception object. For example,

>              class ShipDestroyed extends RuntimeException {
>                 Ship ship;  // Which ship was destroyed.
>                 int where_x, where_y;  // Location where ship was destroyed.
>                 ShipDestroyed(String message, Ship s, int x, int y) {
>                       // Constructor:  Create a ShipDestroyed object
>                       // carrying an error message and the information
>                       // that the ship s was destroyed at location (x,y)
>                       // on the screen.
>                     super(message);
>                     ship = s;
>                     where_x = x;
>                     where_y = y;
>                 }
>              }

> Here, a `ShipDestroyed` object contains an error message and some information about a ship that was destroyed. This could be used, for example, in a statement:

>          if ( userShip.isHit() )
>             throw new ShipDestroyed("You've been hit!", userShip, xPos, yPos);

> Note that the condition represented by a `ShipDestroyed` object might not even be considered an error. It could be just an expected interruption to the normal flow of a game. Exceptions can sometimes be used to handle such interruptions neatly.

> ### Exceptions in Subroutines and Classes

> The ability to throw exceptions is particularly useful in writing general-purpose subroutines and classes that are meant to be used in more than one program. In this case, the person writing the subroutine or class often has no reasonable way of handling the error, since that person has no way of knowing exactly how the subroutine or class will be used. In such circumstances, a novice programmer is often tempted to print an error message and forge ahead, but this is almost never satisfactory since it can lead to unpredictable results down the line. Printing an error message and terminating the program is almost as bad, since it gives the program no chance to handle the error.

> The program that calls the subroutine or uses the class needs to know that the error has occurred. In languages that do not support exceptions, the only alternative is to return some special value or to set the value of some variable to indicate that an error has occurred. For example, the `readMeasurement()` function in Section 2 returns the value `-1` if the user's input is illegal. However, this only works if the main program bothers to test the return value. And in this case, using `-1` as a signal that an error has occurred makes it impossible to allow negative measurements. Exceptions are a cleaner way for a subroutine to react when it encounters an error.

> It is easy to modify the `readMeasurement()` subroutine to use exceptions instead of a special return value to signal an error. My modified subroutine throws a `ParseError` when the user's input is illegal, where `ParseError` is the subclass of `Exception` that was defined earlier in this section. (Arguably, it might be more reasonable to avoid defining a new class by using the standard exception class `IllegalArgumentException` instead.) The changes from the original version are shown in red:

>        static double readMeasurement() throws ParseError {

>              // Reads the user's input measurement from one line of input.
>              // Precondition:   The input line is not empty.
>              // Postcondition:  The measurement is converted to inches and
>              //                 returned.  However, if the input is not legal,
>              //                 a ParseError is thrown.
>              // Note:  The end-of-line is NOT read by this routine.

>           double inches;  // Total number of inches in user's measurement.

>           double measurement;  // One measurement,
>                                //   such as the 12 in "12 miles"
>           String units;        // The units specified for the measurement,
>                                //   such as "miles"

>           char ch;  // Used to peek at next character in the user's input.

>           inches = 0;  // No inches have yet been read.

>           skipBlanks();
>           ch = TextIO.peek();

>           /* As long as there is more input on the line, read a measurement and
>              add the equivalent number of inches to the variable, inches.  If an
>              error is detected during the loop, end the subroutine immediately
>              by throwing a ParseError. */

>           while (ch != '\n') {

>               /* Get the next measurement and the units.  Before reading
>                  anything, make sure that a legal value is there to read. */

>               if ( ! Character.isDigit(ch) ) {
>                   throw new ParseError(
>                                "Expected to find a number, but found " + ch);
>               }
>               measurement = TextIO.getDouble();

>               skipBlanks();
>               if (TextIO.peek() == '\n') {
>                  throw new ParseError(
>                               "Missing unit of measure at end of line.");
>               }
>               units = TextIO.getWord();
>               units = units.toLowerCase();

>               /* Convert the measurement to inches and add it to the total. */

>               if (units.equals("inch")
>                       || units.equals("inches") || units.equals("in")) {
>                   inches += measurement;
>               }
>               else if (units.equals("foot")
>                          || units.equals("feet") || units.equals("ft")) {
>                   inches += measurement * 12;
>               }
>               else if (units.equals("yard")
>                          || units.equals("yards") || units.equals("yd")) {
>                   inches += measurement * 36;
>               }
>               else if (units.equals("mile")
>                          || units.equals("miles") || units.equals("mi")) {
>                   inches += measurement * 12 * 5280;
>               }
>               else {
>                   throw new ParseError("\"" + units
>                                 + "\" is not a legal unit of measure.");
>               }

>               /* Look ahead to see whether the next thing on the line is
>                  the end-of-line. */

>               skipBlanks();
>               ch = TextIO.peek();

>           }  // end while

>           return inches;

>        } // end readMeasurement()

> In the main program, this subroutine is called in a `try` statement of the form

>               try {
>                  inches = readMeasurement();
>               }
>               catch (ParseError e) {
>                  . . .  // Handle the error.
>               }

> The complete program can be found in the file LengthConverter3.java. From the user's point of view, this program has exactly the same behavior as the program LengthConverter2 from Section 2, so I will not include an applet version of the program here. Internally, however, the programs are different, since LengthConverter3 uses exception-handling.

> ### Assertions

> Recall that a precondition is a condition that must be true at a certain point in a program, for the execution of the program to continue correctly from that point. In the case where there is a chance that the precondition might not be satisfied, it's a good idea to insert an `if` statement to test it. But then the question arises, What should be done if the precondition does not hold? One option is to throw an exception. This will terminate the program, unless the exception is caught and handled elsewhere in the program.

> The programming languages C and C++ have a facility for adding assertions to a program. These assertions take the form `assert(`condition`)`, where condition is a `boolean`-valued expression. This condition expresses a precondition that must hold at that point in the program. When the computer encounters an assertion during the execution of the program, it evaluates the condition. If the condition is false, the program is terminated. Otherwise, the program continues normally. Assertions of this form are not available in Java, but something similar can be done with exceptions. The Java equivalent of `assert(`condition`)` is:

>                if (condition == false)
>                    throw new IllegalArgumentException("Assertion Failed.");

> Of course, you could use a better error message. And it would be better style to define a new exception class instead of using the standard class `IllegalArgumentException`.

> Assertions are most useful during testing and debugging. Once you release your program, you don't really want it to crash. Still, many programs are released with a main program that says, essentially

>         try {
>             .
>             .  // Run the program.
>             .
>         }
>         catch (Exception e) {
>            System.out.println("An unexpected internal error has occurred.");
>            System.out.println("Please submit a bug report to the programmer.");
>            System.out.println("Details of the error:"):
>            e.printStackTrace();
>         }

> If a program contains a large number of assertions, they might slow the program down significantly. One advantage of assertions in C and C++ is that they can be "turned off." That is, if the program is compiled in one way, then the assertions are included in the compiled code. If the program is compiled in another way, the assertions are not included. During debugging, the first type of compilation is used. The release version of the program is compiled with assertions turned off. The release version will be more efficient, because the computer won't have to evaluate all the assertions. The nice part is that the source code doesn't have to be modified to produce the release version.

> There is something similar that might work in Java, depending on how smart your compiler is. Suppose that you define a constant

>            static final boolean DEBUG = true;

> and express your assertions as:

>                if (DEBUG == true && condition == false)
>                    throw new IllegalArgumentException("Assertion Failed.");

> Since `DEBUG` is true, the value of "`DEBUG == true &&` condition`== false`" is the same as the value of condition, so this `if` statement still works as a test of the precondition. Now suppose you are finished debugging. Before you compile the release version of the program, change the definition of `DEBUG` to

>            static final boolean DEBUG = false;

> Now, the value of "`DEBUG == true &&` condition`== false`" has to be `false`, and a smart compiler can tell this at compilation time. Given that the condition in the `if` statement is known to be false, a smart compiler will not even bother to include the `if` statement in the compiled code, since it would not be executed in any case. So, the compiled code for the release version will be shorter and more efficient than the debugging version. And you only had to change one line in the source code!

> End of Chapter 9

[ Next Chapter | Previous Section | Chapter Index | Main Index ]