< Day Day Up > |
Now that we've studied the behavior of properties in relation to inheritance, let's turn our attention to constructors, which we first discussed in Chapter 4. A constructor function initializes the instances of a class by:
When a class is extended, the subclass can define a constructor function of its own. A subclass constructor is expected to:
In all inheritance relationships, setup and property initialization relating to the superclass occur in the superclass constructor, not in the subclass constructor.
For example, here is a simple subclass function definition: class A { public function A ( ) { } } class B extends A { // Subclass constructor public function B ( ) { // Invoke superclass's constructor function explicitly super( ); } } The following two constructor function definitions are functionally synonymous. In the first case, we provide a call to super( ) explicitly; in the second case, the compiler provides the call to super( ) implicitly: public function B ( ) { // Invoke superclass's constructor function explicitly. super( ); } public function B ( ) { // No constructor call. The compiler provides one implicitly. } In general, it's good form to always provide a call to super( ) in a subclass constructor, even if that call has no arguments. If you intentionally omit the call to super( ) , be sure to add a comment explaining why; otherwise , it may look as if you simply forgot it. If a subclass does not define a constructor function at all, the compiler automatically creates one and adds a call to super( ) as its only statement. Hence, the following two definitions of the class B are functionally identical; the first is an explicit version of what the compiler creates automatically in the second: // Explicitly provide constructor class B extends A { // Declare a constructor explicitly public function B ( ) { // Invoke superclass's constructor function explicitly super( ); } } // Let compiler create default constructor automatically class B extends A { } Invoking super( ) anywhere but as the first statement of a subclass constructor function causes the following error: The superconstructor must be called first in the constructor body. Furthermore, super( ) must not be used twice in a constructor function. (Using super( ) twice also yields the preceding error message because the second use is not the constructor's first statement.) Restricting super( ) to the first line of a constructor function has the following benefits:
A subclass constructor function can (and often does) define different parameters than its superclass counterpart . This technique allows a subclass to alter the way its superclass instantiates objects. For example, suppose a Rectangle class defines a constructor with width and height parameters. A Square subclass could provide its own constructor that defines a single side parameter (squares have the same width and height, so specifying both is redundant). Example 6-3 shows the code. Example 6-3. The Rectangle and Square constructorsclass Rectangle { private var w:Number = 0; private var h:Number = 0; // Rectangle constructor public function Rectangle (width:Number, height:Number) { setSize(width, height); } public function setSize (newW:Number, newH:Number):Void { w = newW; h = newH; } public function getArea ( ):Number { return w * h; } } class Square extends Rectangle { // Square constructor public function Square (side:Number) { // Pass the side parameter onto the Rectangle constructor. super(side, side); } // Earlier in this chapter, we implemented the Square.setSize( ) method // with two parameters ( newH and newW ). This time we'll streamline // the method, allowing only a single parameter to be passed. public function setSize (side:Number):Void { super.setSize(side, side); } } When defining a subclass's constructor function, be careful not to unintentionally disable the behavior of a superclass's constructor. In the following example, the ColoredBall class erroneously defines a constructor function that doesn't supply necessary information to its superclass's constructor function: class Ball { private var r:Number; public function Ball (radius:Number) { r = radius; } } class ColoredBall extends Ball { private var c:Number; // Here's the problematic constructor... public function ColoredBall (color:Number) { // OOPs! No call to super( ) , so all ColoredBalls // will start with no radius! c = color; } } Wait a minute. Doesn't the compiler automatically supply the call to super( ) ? Yes, it does; however, the implicit call to super( ) doesn't include the necessary radius parameter. As written, the ColoredBall constructor does not allow us to create a ColoredBall instance with an initial radius defined. Therefore, the ColoredBall constructor must not call only super( ) , but it must also accept radius as a parameter and pass it on to the superclass constructor. Here's the corrected version of ColoredBall , which preserves the Ball constructor's behavior: class ColoredBall extends Ball { private var c:Number; // All fixed up... public function ColoredBall (radius:Number, color:Number) { super(radius); c = color; } } Notice that, as a matter of good form, we list the superclass's constructor arguments first (in this case, the radius), then the additional subclass constructor arguments (in this case, the color). 6.5.1 Extraneous Superclass Constructor Invocation in Flash Player 6Due to the historical architecture of ActionScript 1.0, in Flash Player 6-format .swf files, a class's constructor function executes needlessly once for each of its subclasses. For example, the following code shows two simple classes, A and B . Class A 's constructor reports a message to the Output panel when it runs. Class B does nothing but extend class A . class A { public function A ( ) { trace("A's constructor was executed."); } } class B extends A { } Class A has one subclass, B ; hence, in a Flash Player 6-format .swf file, class A 's constructor executes once, needlessly, even if no instance of A or B is ever created. For example, if the following code appears in a .fla file, then A 's constructor will run: var bInstance:B; Extraneous constructor executions can perform unwanted tasks, such as attaching a movie clip or incrementing an instance counter. To prevent unwanted code execution, use a parameter that indicates that the constructor is being called intentionally. For example: class A { public function A (doConstruct:Boolean) { if (doConstruct) { // Normal constructor code goes here. } } } class B extends A { public function B ( ) { // Pass true to the constructor to tell it to perform initialization. super(true); } } This problem does not affect Flash Player 7-format .swf files that use ActionScript 2.0. For much more information, see Chapter 12 of ActionScript for Flash MX: The Definitive Guide (O'Reilly). |
< Day Day Up > |