10.1 The Exception-Handling Cycle

 <  Day Day Up  >  

To learn how to dispatch and respond to errors in our own programs, we'll return to the Box class example from Chapter 4. Recall that in the Box class, we defined a setWidth( ) method to set the width property of a Box instance. The setWidth( ) method checks whether a new width is within legal range before changing the width property of the Box instance. If the new width specified is not valid, no change is made, and the method signals a problem by returning the value false . The relevant Box class code follows . (Note that the Box class code in this example is incomplete; for now we're concentrating only on the portions of the class that deal with setting the width property.)

 class Box {   private var width:Number;   public function setWidth (w:Number):Boolean {     if ((isNaN(w)  w == null)  (w <= 0  w > Number.MAX_VALUE)) {       // Invalid data, so quit.       return false;     }     width = w;     return true;   } } 

When setWidth( ) returns the value false , it does so to indicate an error condition. Code that invokes the setWidth( ) method is expected to check setWidth( ) 's return value and respond gracefully if there's a problem (i.e., if setWidth( ) returns false ).

Let's now revise the setWidth( ) method so that it generates an exception , a standard form of error supported directly by ActionScript 2.0. Error dispatch and recovery with exceptions works in much the same way as the preceding setWidth( ) method; when a problem occurs, an error is signaled, and some error-recovery code is expected to handle it.

To generate an exception (i.e., signal an error) in our code, we use the throw statement, which takes the following form:

 throw   expression   

where expression is a data value that describes the unusual or problematic situation. Using throw to signal an error is known as "throwing an exception." ActionScript allows any value to act as the expression of a throw statement. For example, the expression value could be the string literal "Something went wrong!" or it could be a numeric error code. However, the best practice is for expression to be an instance of the Error class (or an instance of a subclass of Error ).

The Error class, introduced as a built-in class in Flash Player 7, is a standard class for representing error conditions. Instances of the Error class represent an error (a.k.a. an exception ) in a program.

The Error class defines two public properties, name and message , used to describe the error. The Error class also defines a single method, toString( ) , which returns the value of message or, if message is not defined, returns the string "Error."


Here's one way to write setWidth( ) so that it generates an exception with a throw statement when an invalid width is received:

 public function setWidth (w:Number):Void {   if ((isNaN(w)  w == null)  (w <= 0  w > Number.MAX_VALUE)) {     throw new Error("Invalid width specified.");   }   width = w; } 

When the throw statement executes, program control is immediately transferred to a special section of code that knows how to deal with the problem (we'll discuss this shortly). In official terms, the code that deals with the problem is said to handle the exception .

In our revised setWidth( ) method, when the value of w is illegal, we use throw to halt the method, and we pass a new Error object out of the method to the section of code (not yet shown) that will handle the problem:

 throw new Error("Invalid width specified."); 

We also supply a description of the problem ”"Invalid width specified" ”as an argument to the Error constructor. The code block that handles the exception (again, not yet shown) uses the Error instance to diagnose what went wrong. Notice that setWidth( ) no longer returns a Boolean success or failure value. If the method encounters a problem, it uses the throw statement to both quit and signal a failure. Otherwise, it completes normally and we can rest assured that it performed its job successfully.

Now that our setWidth( ) method includes the throw statement (i.e., now that it might generate an exception), we must adjust the way we invoke it. Previously, we would have used the return value of setWidth( ) to determine what to do in the event of a problem:

 var b:Box = new Box( ); var someWidth:Number = -10; // Check   setWidth( )   's return value... if (b.setWidth(someWidth)) {   // ...   setWidth( )   returned   true   , so no problems; proceed as planned.   trace("Width set successfully."); } else {   // ...   setWidth( )   returned   false   ! ERROR! Invalid data. Display a warning.   trace("An error occurred."); } 

But now, when invoking the new exception-based version of our method, we don't bother checking the return value of setWidth( ) . Instead, we set up a code branch using the formal try/catch/finally statement instead of an if/else statement. Here's how the new version looks:

 var b:Box = new Box( ); var someWidth:Number = -10; try {   b.setWidth(someWidth);   // If we get this far, no exception occurred; proceed as planned.   trace("Width set successfully."); } catch (e:Error) {   // ERROR! Invalid data. Display a warning.   trace("An error occurred: " + e.message); } 

Let's study the preceding code in closer detail. The try keyword tells the interpreter that we're about to execute some code that might generate an exception:

 try {   // Code here might cause an exception. } 

In this case, the code we're executing is setWidth( ) :

 try {   b.setWidth(someWidth);   // If we get this far, no exception occurred; proceed as planned.   trace("Width set successfully."); } 

The catch block handles exceptions generated by the try block. That is, the code in the catch block executes if, and only if, code in the try block generates an exception:

 } catch (e:Error) {   // ERROR! Invalid data. Display a warning.   trace("An error occurred: " + e.message); } 

When we invoke b.setWidth( ) within the try block, if setWidth( ) 's throw statement doesn't execute (i.e., if no error occurs), then the subsequent statements in the try block execute normally and the program skips the catch block entirely. But if setWidth( ) throws an exception, the program immediately skips to and executes the catch block.

Notice, therefore, the typical structure:

  • Code in a try clause invokes a function that might throw an exception.

  • Code in the invoked function throws an exception using the throw statement if an error occurs.

  • Control returns either to the try block (if no error is thrown) or the catch block (if an error is thrown). The catch block deals with any errors that occur in the try block.

When the catch block is executed, it receives the expression of the throw statement as a parameter. In our present example, within the catch block, the parameter e stores the Error instance created by the throw statement in the Box.setWidth( ) method:

 // Here's the throw statement, excerpted from   setWidth( )   . throw new Error("Invalid width specified."); 

We can use that Error instance to help diagnose the problem. The string passed to the Error constructor is available via the message property of the Error instance. In our example catch statement, we simply display the Error instance's message in the Output panel, as follows:

 trace("An error occurred: " + e.message); 

The Error class's toString( ) method, which is called automatically when an instance is used in a string context, returns the value of the message property. Hence, in a string context, e and e.message are equivalent. For example, the following two statements are synonymous:

 trace("An error occurred: " + e.message); trace("An error occurred: " + e); 

We'll use them interchangeably throughout this chapter.

Note that the parameter listed in a catch block should not need to be declared as a variable before being used. However, due to a bug in version 7.0 of the Flash MX 2004 authoring tool, the compiler (incorrectly) generates an error when a catch block parameter is referenced without first being declared. For example, in version 7.0 of Flash MX 2004, the following code:

 public function someMethod ( ):Void {   try {     throw new Error("Some error message.");   } catch (e:Error) {     // Respond to the error.     trace("An error occurred: " + e);   } } 

(incorrectly) generates the following error:

 There is no property with the name 'e'. 

This bug is fixed in the Flash MX 2004 updater, available at: http://macromedia.com/support/flash/downloads.html. (To work around the bug without installing the updater , simply declare the parameter e as a variable.)

Metaphorically, the code that detects a problem throws an exception ( passes an Error object) to the catch block, which receives it as a parameter ( catches it).

Incidentally, the try block can throw an error directly. For example, in the following code, the catch block is executed when x divided by y is the numeric value NaN (as is the case when both x and y are 0):

 var x:Number = 0; var y:Number = 0; var e:Error;  // Declare   e   as parameter to avoid compiler bug try {   if (isNaN(x/y)) {     throw new Error("Quotient is NaN.");   } else {     trace ("Result is " + String(x/y));   } } catch (e:Error) {   trace("Error: " + e.message); } 

In the preceding example, you might think that attempting to divide by 0 (when y is 0) would cause ActionScript itself to throw a "Division by zero" exception, but no such luck. ActionScript doesn't throw exceptions. It is up to the developer to check for error conditions and invoke throw as desired. Furthermore, in ActionScript, dividing anything other than 0 by 0 yields Infinity (for positive numerators) or -Infinity (for negative numerators).

Whatever the case, it's more common for try blocks to invoke methods that throw exceptions than for a try block to include a throw statement directly. Later, under "Exception Bubbling," we'll learn more about how errors are transferred from methods to enclosing try blocks. For now, you can simply rely on the rules in the following tip.

Within a try block, if a statement executes, you can safely trust that all preceding statements have executed successfully. If code in a try block (or a method invoked in the try block) throws an error, the remaining statements in the try block are skipped and the statements in the catch block are executed. If no exception is thrown, the try block completes and execution resumes with the statements immediately following the try/catch/finally statement.


To find out what happens if the error is never caught (or, synonymously, never trapped ), see "Uncaught Exceptions," later in this chapter.

 <  Day Day Up  >  


Essential ActionScript 2.0
Essential ActionScript 2.0
ISBN: 0596006527
EAN: 2147483647
Year: 2004
Pages: 177
Authors: Colin Moock

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net