10.5 Nested Exceptions

 <  Day Day Up  >  

So far we've used only single-level try/catch/finally statements, but exception-handling logic can also be nested. A try/catch/finally statement can appear inside the try , catch , or finally block of another try/catch/finally statement. This hierarchical nesting allows any block in a try/catch/finally statement to execute code that might, itself, throw an error.

For example, suppose we were writing a multiuser, web-based message board system. We define a core class for the application (called BulletinBoard) , another class that manages the user interface (called GUIManager) , and another class that represents a user on the board (called User) . We give BulletinBoard a method, populateUserList( ) , which displays the list of current active users. The populateUserList( ) method splits its work into two stages: first it retrieves a List component from the application's GUIManager instance, then it populates that List with users from a supplied array of User instances. These two stages can both potentially generate an exception, so a nested try/catch/finally structure is used in the populateUserList( ) method. Let's take a closer look at this nested structure.

During the first stage of populateUserList( ) , if the List component isn't available, a UserListNotFound exception is thrown by the GUIManager . The UserListNotFound exception is caught by the outer try/catch/finally statement.

If, on the other hand, the List component is available, the populateUserList( ) method proceeds with stage two, during which a loop populates the List component with users from the supplied array. For each iteration through the loop, if the current user's ID cannot be found, the User.getID( ) method throws a UserIdNotSet exception. The UserIdNotSet exception is caught by the nested try/catch/finally statement.

Here's the code (we won't work closely with components until Chapter 12, so if some of this code looks new to you, try returning to it once you're finished reading that chapter):

 public function populateUserList (users:Array):Void {   try {     // Start stage 1...get the List.     // If   getUserList( )   throws an exception, the outer   catch   executes.     var ulist:List = getGUIManager( ).getUserList( );     // Start stage 2...populate the List.     for (var i:Number = 0; i < users.length; i++) {       try {         var thisUser:User = User(users[i]);         // If   getID( )   throws an exception, the nested   catch   executes.         ulist.addItem(thisUser.getName( ), thisUser.getID( ));       } catch (e:UserIdNotSet) {         trace(e);         log.warn(e);         continue;  // Skip this user.       }     }   } catch (e:UserListNotFound) {     trace(e);     log.warn(e);   } } 

Now that we've had a look at a specific nested exception example, let's consider how nested exceptions are handled in general.

If an exception occurs in a try block that is nested within another try block, and the inner try block has a catch block that can handle the exception, then the inner catch block is executed and the program resumes at the end of the inner try/catch/finally statement.

 try {   try {     // Exception occurs here.     throw new Error("Test error");   } catch (e:Error) {     // Exception is handled here.     trace(e);  // Displays: Test error   }   // The program resumes here. } catch (e:Error) {   // Handle exceptions generated by the outer   try   block. } 

If, on the other hand, an exception occurs in a try block that is nested within another try block, but the inner try block does not have a catch block that can handle the exception, then the exception bubbles up to the outer try/catch/finally statement (and, if necessary, up the call stack) until a suitable catch block is found or the exception is not caught. If the exception is caught somewhere in the call stack, the program resumes at the end of the try/catch/finally statement that handled the exception. Note that in the following code example (and subsequent examples), the hypothetical error datatype SomeSpecificError is a placeholder used to force the thrown exception to not be caught. In order to test the code example in your own code, you'd have to create a subclass of Error called SomeSpecificError .

 try {   try {     // Exception occurs here.     throw new Error("Test error");   } catch (e:SomeSpecificError) {     // Exception is not handled here.     trace(e);  // Never executes because the types don't match.   } } catch (e:Error) {   // Exception is handled here.   trace(e);  // Displays: Test error } // The program resumes here, immediately after the outer   catch   block // has handled the exception. 

If an exception occurs in a try block that is nested within a catch block, and the inner try block does not have a catch block that can handle the exception, then the search for a matching catch block begins outside the outer try/catch/finally statement:

 try {   // Outer exception occurs here.   throw new Error("Test error 1"); } catch (e:Error) {   // Outer exception handled here.   trace(e);  // Displays: Test error 1   try {     // Inner exception occurs here.     throw new Error("Test error 2");   } catch (e:SomeSpecificError) {     // Inner exception is not handled here.     trace(e);  // Never executes because the types don't match.   } } // The search for a matching   catch   block for the inner exception starts here. 

Last, if an exception occurs in a try block that is nested within a finally block, but a prior exception is already in the process of bubbling up the call stack, then the new exception is handled before the prior exception continues to bubble up.

 // This method throws an exception in a   finally   block. public function throwTwoExceptions ( ):Void {   try {     // Outer exception occurs here. Because there is no   catch   block for this      //   try   block, the outer exception starts to bubble up,      // out of this method.     throw new Error("Test error 1");   } finally {     try {       // Inner exception occurs here. The inner exception is        // handled before the outer exception actually bubbles up.       throw new Error("Test error 2");     } catch (e:Error) {       // Inner exception is handled here.       trace("Internal catch: " + e);     }   } } // Elsewhere, within another method that calls the preceding method. try {   throwTwoExceptions( ); } catch (e:Error) {   // The outer exception, which has bubbled up from   throwTwoExceptions( )   ,    // is handled here.   trace("External catch: " + e); } // Output (notice that the inner exception is caught first): Internal catch: Test error 2 External catch: Test error 1 

If, in the preceding example, the exception thrown in the finally block had never been caught, then the interpreter would have sent it to the Output panel and aborted all other code in the call stack. As a result, the original, outer exception would have been discarded along with all code in the call stack. The following code demonstrates the preceding principle. It throws an uncaught exception from a finally statement. As a result, the exception thrown by the outer try block is discarded. To test the code, you can run it directly on a frame in the timeline.

 try {   // Outer exception occurs here.   throw new Error("Test error 1"); } finally {   try {     // Inner exception occurs here.     throw new Error("Test error 2");   } catch (e:SomeSpecificError) {     // Inner exception is not handled here.     trace("internal catch: " + e); // Never executes because                                     // the types don't match.   } } // The search for a matching   catch   block for the inner exception starts  // here. If no match is ever found, then the Output panel displays  // "Test error 2", and the bubbling of the outer exception is aborted. 

The preceding code demonstrates the effect of an uncaught exception in one scenario, but once again, it's not appropriate to allow an exception to go uncaught. In the preceding case, we should either catch the exception or revise our code so that the exception isn't thrown in the first place.

10.5.1 A Nested Exception Bug

Earlier we learned that finally executes no matter what happens in a try block. In theory, that should be true. However, a bug in Flash Player 7 prevents finally from executing when a nested try/catch/finally statement returns from a function, as shown in the following code:

 function nestedFinallyBugDemo ( ):Void {   try {     try {       throw new Error("Test error");     } finally {       trace("Inner finally block.");       return;     }   } finally {     trace("Outer finally block.");  // Due to a bug in Flash Player 7,                                      // this line never executes.   } } nestedFinallyBugDemo( ); // Displays: Inner finally block. 

The need for this structure is rare, so you probably won't encounter the bug, and Macromedia may very well fix it in Flash Player 8 if not an interim version of Flash Player 7.

 <  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