4.5 Constructor Functions (Take 2)

 <  Day Day Up  >  

We've done pretty well with our example Box class. We've given it methods to invoke and properties to examine and change. Now it's time to return to our Box constructor function, which we introduced much earlier in this chapter under "Constructor Functions (Take 1)."

Suppose we want to make every Box instance we create start out with a width and height of 1. We need something that can set each new Box instance's width and height properties during the instance-creation process. That "something" is our constructor function.

A constructor function is the metaphoric womb of a class; it isn't responsible for creating new instances, but it can be used to initialize each new instance. When we create a new instance of a class using the new operator, the class's constructor function runs. Within the constructor function, we can customize the newly created instance by setting its properties or invoking its methods. The instance can then be delivered to the world tailored to the situation at hand ”perhaps with width and height properties set to 1 (and big, cute, green eyes).

To define a constructor function, we use the function statement within a class body, exactly as we'd define a method. However, a constructor function definition must also observe these rules:

  • The constructor function's name must match its class's name exactly (case sensitivity matters).

  • The constructor function's definition must not specify a return type (not even Void ).

  • The constructor function must not return a value (the return statement is allowed for the sake of exiting the function, provided that no return value is specified).

  • The constructor function's definition must not include the static attribute, but it can be public or private.

Here's another look at the Box class constructor that we created earlier. Once again, lines 2 and 3 are the (empty) function ”the constructor function's body and the rest of the class are omitted from this example:

 class Box {   public function Box ( ) {   } } 

Remember that a constructor's function declaration must not define a return datatype, and the constructor function itself must not return a value. In particular, the constructor function does not return the new instance being created. Creating and returning the new instance is handled automatically by ActionScript as part of the new ClassName ( ) operation. Use the constructor function only for initializing property values or performing other instance initialization.


In ActionScript 2.0, a constructor function's sole purpose is to initialize instances. Constructor functions are not mandatory (as they were in ActionScript 1.0). However, because most classes initialize their instances, most classes include a constructor function. As noted earlier, when a class does not define a constructor function explicitly, ActionScript automatically provides a default constructor that takes no parameters and performs no initialization on new instances of the class. Despite this convenience, as a best practice, always include a constructor, even if it is just an empty one. The empty constructor serves as a formal indication that the class design does not require a constructor and should be accompanied by a comment to that effect. For example:

 class Box {   // Empty constructor. This class does not require initialization.   public function Box ( ) {   } } 

figs/as1note.gif In ActionScript 1.0, a constructor function and a class definition were one in the same. There was no formal class statement ”classes were created simply by defining a constructor function, as in:

 // In ActionScript 1.0, this function both  // defines the   Box   class and serves as  // its constructor function. function Box (w, h) {   this.width = w;   this.height = h; } 

Notice that the constructor function can be declared public or private , just like a normal method. The vast majority of constructor functions are public, but there are specific class designs that require a private constructor (for one example, see Chapter 17). Classes with private constructor functions cannot be instantiated directly. For example, if we supply a private constructor for our Box class:

 class Box {  private  function Box ( ) {   } } 

and then we try to create a Box instance:

 var b:Box = new Box( ); 

the compiler generates the following error:

 The member is private and cannot be accessed. 

In order to allow instances to be created, a class with a private constructor must provide a class method that creates and returns instances. For example:

 class Box {   // Private constructor   private function Box ( ) {   }   // Class method that returns new instances   public static function getBox ( ):Box {     return new Box( );   } } // Usage: var b:Box = Box.getBox( ); 

If a class with a private constructor does not provide a public class method that calls the private constructor internally, you cannot instantiate objects of the class. You might use a private constructor in the following situations:

  • To create a rough equivalent of a Java-style abstract class (i.e., a class that cannot be instantiated, but must be extended to be used, as discussed in Chapter 8)

  • To place limits on when and how a class's instances are created (for example, to prevent a program from creating more than one object from a single class)

While constructors can be declared as public or private , they cannot be declared static . If you specify the static attribute in a constructor function definition, the compiler generates the following error:

 The only attributes allowed for constructor functions are public and private. 

Always be sure that the capitalization of a constructor function's name matches its class name exactly. If you change a class's name from Somename to SomeName but forget to update the constructor function's name to SomeName( ) , ActionScript will no longer consider the constructor function a constructor, and none of your initialization code will run when instances are created with the new operator.


Luckily, the ActionScript compiler warns you as follows when the capitalization of your constructor's name does not match its class name:

 The member function '[FunctionName]' has a different case from the name of  the class being defined, '[ClassName]', and will not be treated as the class  constructor at runtime. 

Let's flesh out our basic Box constructor so that it assigns 1 to the width and height properties of every new Box instance created, as described in the earlier scenario. For clarity, we'll also show the width and height property definitions. By convention (but not by necessity), constructor functions are placed after property definitions but before method definitions. Note also that the constructor declaration does not include a return datatype, as they are prohibited for constructors:

 class Box {   private var width:Number;   private var height:Number;   public function Box ( ) {     // Initialize   width   and   height   width  = 1;     height = 1;   }   // Method definitions typically follow here... } 

Now every time we create a new Box instance, its width and height are initialized to 1 ( otherwise , they'd default to undefined ). That's pretty handy, but is also inflexible . To allow the width and height properties to be customized for each Box instance, we add parameters to our constructor function definition:

 public function Box (w:Number, h:Number) {   // Initialize   width   and   height   , using the    // values passed to the parameters   w   and   h   .   width  = w;   height = h; } 

A constructor function's parameter values are passed to it via the new operator at object-creation time, as follows:

 new   SomeClass   (   value1   ,   value2   ,...   valuen   ); 

where SomeClass is the name of the class being instantiated and value1 , value2 , ... valuen are the values passed to the constructor function. For example, to create a new Box instance with an initial width of 2 and height of 3, we'd use:

 new Box(2, 3); 

Supplying parameter values to a constructor function is the ActionScript equivalent of genetically predetermining that your baby should be a girl, weigh 7 pounds , and have brown hair.

Constructor functions normally use parameter values to set property values, but parameters can also more generally govern what should happen when an instance is created. For example, the constructor function for a Chat class might include a parameter, doConnect , that indicates whether the Chat instance should automatically connect to the chat server upon creation:

 class Chat {   public function Chat (server:String, port:Number, doConnect:Boolean) {     if (doConnect) {       connect(server, port);     }   } } 

4.5.1 Simulating Multiple Constructor Functions

Unlike Java, ActionScript does not support multiple constructor functions for a single class (referred to as overloaded constructors in Java). In Java, a class can initialize an instance differently depending on the number and type of arguments used with the new operator. In ActionScript, similar functionality must be implemented manually. Example 4-5, based on our Box class, shows one possible way to simulate multiple constructor functions in ActionScript. Flash itself uses an analogous technique to allow Date instances to be created from a specific year, month, and day or from a count of milliseconds that have elapsed since January 1, 1970.

In Example 4-5, the Box constructor delegates its work to three pseudo-constructor methods, named boxNoArgs( ) , boxString( ) , and boxNumberNumber( ) . Each pseudo-constructor's name indicates the number and datatype of the parameters it accepts (e.g., boxNumberNumber( ) defines two arguments of type Number ). Note that in this specific example the pseudo-constructors do not define datatypes for their arguments; this anomaly is discussed in the inline code comments.

If some of the code in Example 4-5 is new to you, look for cross-references to related topics in the code comments.

Example 4-5. Simulating overloaded constructors
 class Box {   public var width:Number;   public var height:Number;   /**    * Box constructor. Delegates initialization     * to   boxNoArgs( )   ,   boxString( )   , or   boxNumberNumber( )   .    */   public function Box (a1:Object, a2:Object) {     // As we learned earlier, the   arguments   object stores the      // argument values passed to this function.     // If the constructor was invoked with no arguments, call   boxNoArgs( )   .     // If the constructor was invoked with one string argument,      // call   boxString( )   . If the constructor was invoked with      // two numeric arguments, call   boxNumberNumber( )   .     if (arguments.length == 0) {       boxNoArgs( );     } else if (typeof a1 == "string") {       // In the following line of code, we'd normally have to cast   a1   to the        // type required by the   boxString( )   method's first parameter (in        // this case, String). However, the ActionScript 2.0 cast operator       // does not work with the String and Number datatypes, so,        // unfortunately, we must leave the parameters for   boxString( )   and       //   boxNumberNumber( )   untyped. For details on this casting problem,       // see Chapter 3.        boxString(a1);     } else if (typeof a1 == "number" && typeof a2 == "number") {       // No cast to   Number   here either; see previous comment.       boxNumberNumber(a1, a2);     } else {       // Display a warning that the method was used improperly.       trace("Unexpected number of arguments passed to Box constructor.");     }   }   /**    * No-argument constructor.    */   private function boxNoArgs ( ):Void {     //   arguments.caller   is a reference to the function that called      // this function.     // If this method was not called by the   Box   constructor, then exit.     if (arguments.caller != Box) {       return;     }     // Supply a default width and height.     width  = 1;     height = 1;   }   /**    * String constructor.    */   private function boxString (size):Void {     // If this method was not called by the   Box   constructor, then exit.     if (arguments.caller != Box) {       return;     }     // Set   width   and   height   based on a descriptive string.     if (size == "large") {       width  = 100;       height = 100;     } else if (size == "small") {       width  = 10;       height = 10;     } else {       trace("Invalid box size specified");     }   }   /**    * Numeric constructor.    */   private function boxNumberNumber (w, h):Void {     // If this method was not called by the   Box   constructor, then exit.     if (arguments.caller != Box) {       return;     }     // Set numeric   width   and   height   .     width  = w;     height = h;   } } // Usage: var b1:Box = new Box( ); trace(b1.width);  // Displays: 1 var b2:Box = new Box("large"); trace(b2.width);  // Displays: 100 var b3:Box = new Box(25, 35); trace(b3.width);  // Displays: 25 

4.5.2 Using this in Constructor Functions

Within the body of a constructor function, the this keyword refers to the newly created instance. We use this in a constructor function exactly as we use it from within instance methods. For example, the following code uses this to resolve a parameter/property name conflict:

 public function Box (width:Number, height:Number) {   // Sets   width   property (   this.width   ) to value of   width   parameter (   width   ).   this.width  = width;   // Sets   height   property (   this.height   ) to value    // of   height   parameter (   height   ).   this.height = height; } 

For details on using this , see the earlier discussion under "Referring to the Current Object with the Keyword this ."

4.5.3 Constructor Functions Versus Default Property Values

Earlier in this chapter we learned that an instance property can be assigned a default value provided the value is a compile-time constant expression, such as 10 or "hello world". For example:

 private var x:Number = 10; private var msg:String = "hello world"; 

While it's legal to initialize an instance property by assigning it a default value, it's a best practice to perform all instance property initialization in a constructor function. Constructor functions are not limited by the compile-time constant rule, so they can safely calculate property values with arbitrary code such as method calls, conditionals, and loops . Furthermore, by keeping property initialization in constructors, we make our class's initialization code easy to find and maintain.

 <  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