flash native_classes_non-graphic
Extending non-graphic classes
Other native classes can also be extended in order to increase their capabilities, such as the array class, where the default methods are sometimes not sufficient.
Have you ever wanted to quickly mix the data in an array?
By extending the array class we are going to add a group of practical methods which will be available for all instances of a sub-class. Alongside a new Flash CS3 document we define a MyArray class in a org.bytearray.outils package.
Here is the code of the sub-class MyArray:
package org.bytearray.outils { dynamic public class MyArray extends Array { public function MyArray ( ...rest ) { } } }
The MyArray sub-class is a dynamic class as access to the data using brackets requires the use of a dynamic class.
Extending the Array class uses a little feature that has not been corrected in ActionScript 3. In the constructor of the sub-class we need to add the following line:
package org.bytearray.outils { dynamic public class MyArray extends Array { public function MyArray ( ...rest ) { splice.apply(this, [0, 0].concat(rest)); } } }
This feature allows you to initialize the array when the parameters are passed to the constructor.
At this point we benefit from an operational sub-class which has all of the capacities of a standard array:
// importing the MyArray class import org.bytearray.outils.MyArray; // creation of an array of numbers var firstArray:MyArray = new MyArray (58, 48, 10); // displays : 48 trace( firstArray[1] ); // adding a value firstArray.push ( 25 ); // displays : 4 trace( firstArray.length ); // displays : 25 trace( firstArray.pop() ) ; // displays : 3 trace( firstArray.length );
For the moment, the use of the MyArray sub-class is not really justified, it doesn’t have any more functionalities than the Array class.
In order to copy an array, we could be tempted to write the following code:
// creation of a first array var MyArray:Array = new Array (58, 48, 10); // creation of a copy? var myCopy:Array = MyArray;
This mistake makes reference to the idea of composite and primitive variables that we discussed in the chapter entitled Language and API of the Flash player. Remember, when copying composite data, we copy the variables by reference and not by value.
In order to create a real copy of the array we define a new copy method in the MyArray class:
package org.bytearray.outils { dynamic public class MyArray extends Array { public function MyArray ( ...rest ) { splice.apply(this, [0, 0].concat(rest)); } public function copy ( ):MyArray { var myCopy:MyArray = new MyArray(); var lng:int = length; for ( var i:int = 0; i< lng; i++ ) myCopy[i] = this[i]; return myCopy; } } }
By testing the following code, we see that a real copy of the array is returned by the copy method:
// importing the MyArray class import org.bytearray.outils.MyArray; // creation of an array of numbers var array:MyArray = new MyArray (58, 48, 10); // creation of a copy var copyArray:MyArray = array.copy(); // modification of a value copyArray[0] = 100; // displays : 12,58,85 100,58,85 trace ( array, copyArray );
We could have used the Array.slice method but this would have returned an Array type array and not a new instance of MyArray. When modifying a value of the array we can see that the two arrays are very distinct.
This method does not work just for the copy of the arrays containing primitive type data. If we want to copy the arrays containing composite type data we use the writeObject method of the flash.utils.ByteArray class. We discuss this further in the chapter entitled ByteArray.
We will now add a new mix method allowing us to rearrange the array’s data:
public function mix ( ):void { var i:int = length; var random:int; var current:*; while ( i-- ) { random = Math.floor ( Math.random()*length ); current = this[i]; this[i] = this[random]; this[random] = current; } }
In the following code we mix different values, we could use this method in a game for example. We could have a series of questions stored in an array and on each launch the array is mixed in order for the players to never get the questions in the same order:
// importing the MyArray class import org.bytearray.outils.MyArray; // creation of an array of numbers var array:MyArray = new MyArray(58, 48, 10); // mix values array.mix(); // displays : 85,12,58 trace( array );
The equal method allows us to test if two arrays contain the same values:
public function equal ( pArray:Array ):Boolean { if ( this == pArray ) return true; var i:int = this.length; if ( i != pArray.length ) return false; while( i-- ) { if ( this[i] != pArray[i] ) return false; } return true; }
In the following code we compare two arrays:
// importing the MyArray class import org.bytearray.outils.MyArray; // creation of an array of numbers var firstArray:MyArray = new MyArray(12, 58, 85); // creation of another array of numbers var secondArray:MyArray = new MyArray(12, 58, 85); // displays : true trace( firstArray.equal ( secondArray ) );
When the array contains the same values the equal method returns true. Otherwise if we modify the data in the first array the equal method returns false:
// importing the MyArray class import org.bytearray.outils.MyArray; // creation of an array of numbers var firstArray:MyArray = new MyArray(100, 58, 85); // creation of another array of numbers var secondArray:MyArray = new MyArray(12, 58, 85); // displays : false trace( firstArray.equal ( secondArray ) );
In this example we don’t take into account arrays having different sizes. You could use different approaches to this – it’s up to you! It would be great to have a method which calculated the average value contained in the array. Before starting up, the calculation should ensure that the array contains only numbers.
To do this we use the new method every in the array class:
public function average ( ):Number { if ( ! every ( filter ) ) throw new TypeError ("Le array current ne contient pas que des nombres"); var i:int = length; var sum:Number = 0; while ( i-- ) sum += this[i]; return sum/length; } private function filter ( pElement:*, pIndex:int, pArray:Array ):Boolean { return pElement is Number; }
When the method average is executed, it verifies if all the data in the array are indeed numbers. To do this the filter method is executed on each element of the array and returns true when the searched element is a number. If it is not the case it returns false and we raise a TypeError. If no error is raised we can start the calculation of the average.
In the following code the calculation is possible:
// importing the MyArray class import org.bytearray.outils.MyArray; // creation of an array of numbers var firstArray:MyArray = new MyArray(100, 58, 85); // displays : 81 trace( firstArray.average() );
On the contrary, if a value of an array is not a number:
// importing the MyArray class import org.bytearray.outils.MyArray; // creation of an array of numbers var firstArray:MyArray = new MyArray(this, 58, false); trace( firstArray.average() );
An execution error is raised:
TypeError: The array doesn’t only contain numbers
In order to manage the error we can place the call of the average method in the try catch block:
// importing the MyArray class import org.bytearray.outils.MyArray; // creation of an array of numbers var firstArray:MyArray = new MyArray(this, 58, false); try { trace( firstArray.average() ); } catch ( pError:Error ) { trace("there is a calculation error!"); }
We can add as many methods as we want to the MyArray class. It is up to you to add the functionalities that you want. This allows us to substitute the myArray class with the Array class in order to still have these functionalities available.
Worth remembering
- All native classes are not sub-classable.
- Composition can be used in order to increase the capabilities of a class that is not sub-classable.
- Extending a native class is an elegant way of extending the capacities of ActionScript 3 or the player.
Mediabox Training Centre © 2000 - 2008 All rights reserved.
Adobe Authorized Training Centre. State convention under number 25 14 02167 14.
Mediabox : SARL au capital de 62.000€ - Activity number: 25 14 02167 14 - SIRET : 493 716 468 00027
MEDIABOX, 102 Avenue des Champs Elysées, 75008 PARIS - Tel. +33(0)2.31.91.96.89 - Fax. +33(0)2.72.68.56.42


