10.4 The finally Block

 <  Day Day Up  >  

So far, we've discussed only the try and catch blocks in the try/catch/finally statement. As we've seen, a try block contains code that might throw an exception, and a catch block executes code in response to a thrown exception. The finally block, by comparison, always executes, whether or not code in the try block throws an exception.

The finally block is placed once (and only once) as the last block in a try/catch/finally statement. For example:

 try {   // Statements } catch (   e   :   ErrorType1   ) {   // Handle   ErrorType1   exceptions. } catch (   e   :   ErrorTypen   ) {   // Handle   ErrorTypen   exceptions. } finally {   // This code always executes, no matter how the   try   block exits. } 

Misplacing the finally block causes a compile-time error.

In the preceding code, the finally block executes in one of the four circumstances:

  • Immediately after the try block completes without errors

  • Immediately after a catch block handles an exception generated in the try block

  • Immediately before an uncaught exception bubbles up

  • Immediately before a return , continue , or break statement transfers control out of the try or catch blocks (but see the bug described under "A Nested Exception Bug," later in this chapter)

The finally block of a try/catch/finally statement typically contains cleanup code that must execute whether or not an exception occurs in the corresponding try block. For example, suppose we're creating a space shooter game and we define a class, SpaceShip , to represent spaceships in the game. The SpaceShip class has a method, attackEnemy( ) that performs the following tasks :

  • Sets the spaceship's current target

  • Fires on that target

  • Clears the current target (i.e., sets the SpaceShip 's currentTarget property to null )

In our hypothetical application, we'll assume that the first two of the preceding tasks might generate an exception. Further, we'll assume that the attackEnemy( ) method doesn't handle those exceptions itself; instead, it passes them up to the calling method. But whether or not an exception is generated, the attackEnemy( ) method must set the currentTarget property to null .

Here's what the attackEnemy( ) method would look like if we coded it with a catch statement (i.e., without using finally ):

 public function attackEnemy (enemy:SpaceShip):Void {   try {     setCurrentTarget(enemy);     fireOnCurrentTarget( );   } catch (e:Error) {     // Clear the current target if an exception occurs.     setCurrentTarget(null);      // Pass the exception up to the calling method.     throw e;                    }   // Clear the current target if no exception occurs.   setCurrentTarget(null);     } 

Notice that we must duplicate the statement, setCurrentTarget(null) . We place it both in the catch block and after the try/catch statement, guaranteeing that it will run whether or not there's an exception in the try block. But duplicating the statement is error prone. In the preceding method, a programmer could have easily forgotten to clear the current target after the try/catch block.

If we change our strategy by clearing the current target in a finally block, we remove the redundancy in the preceding code:

 public function attackEnemy (enemy:SpaceShip):Void {   try {     setCurrentTarget(enemy);     fireOnCurrentTarget( );   } finally {     setCurrentTarget(null);   } } 

In the revised version, the finally block clears the current target whether there's an exception or not. Because both situations are handled, we no longer have any need for a catch block; we can simply let the exception bubble up to the calling method automatically.

You might be wondering why we need the finally block at all. That is, why not just use the following code?

 // This code might look decent, but there's a problem. Can you spot it? public function attackEnemy (enemy:SpaceShip):Void {   setCurrentTarget(enemy);   fireOnCurrentTarget( );   setCurrentTarget(null); } 

Remember that when an exception is thrown, program control is transferred to the nearest suitable catch block in the call stack. Hence, if fireOnCurrentTarget( ) throws an exception, control transfers out of attackEnemy( ) , never to return, and setCurrentTarget(null) would never execute. By using a finally block, we guarantee that setCurrentTarget(null) executes before the exception bubbles up.

The attackEnemy( ) method example reflects the most common use of finally in multithreaded languages like Java, where a program can have multiple sections of code executing simultaneously . In Java, the following general structure is commonplace ”it guards against the possibility that an object busy with a task might be inappropriately altered by another object during the execution of that task:

 // Set a state indicating this object's current task. // External objects should check this object's state  // before accessing or manipulating this object. doingSomething = true;      try {   // Perform the task.   doSomething( );            } finally {   // Unset the "in-task" state (whether or not    // the task generated an exception).   doingSomething = false;   } 

In ActionScript, the preceding state-management code is effectively unnecessary because the language is single-threaded, so no external object will ever attempt to alter the state of an object while it is busy executing a method. Hence, finally is used much more rarely in ActionScript than it is in multithreaded languages. However, it can still be used for organizational purposes, to contain code that performs cleanup duties after other code has executed.

 <  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