3.5 Circumventing Type Checking

 <  Day Day Up  >  

We learned earlier that ActionScript 2.0's type checking is an "opt-in" system. That is, type checking occurs only when the programmer supplies type information (i.e., declares an item's datatype) in a program's source code. If you are sadistic and prefer to check your code manually for type errors, simply do not declare type information for variables , properties, parameters, and return values. When no type information is supplied for a data container, the compiler skips type checking for it. For example, the following code creates a variable, x , but does not declare x 's datatype:

 var x = 10; 

Because the variable x is not typed, type checking is not (indeed cannot be) performed, and x 's datatype can be changed without generating an error:

 x = "hello";  // Change from Number to String. 

Furthermore, accessing nonexistent properties and methods on x causes no error:

 trace(x._width);   // No error. Returns   undefined   if   _width   doesn't exist. x.flyToTheMoon( );  // No error.                    // Fails silently if   flyToTheMoon( )   doesn't exist. 

Likewise, when a function or method parameter's type is not supplied, the parameter can accept values of any type without generating a compile-time error. And when a function's or method's return type is not supplied, the function or method can return values of any type without generating a compile-time error. The following code shows a method that declares no parameter types or return type. No matter what the types of the parameters, the method adds them together and returns the result. The result can legally belong to any datatype:

 function combine (x, y) {   return x + y; } // Pass values with different datatypes to   x   and   y   . // No errors occur because the parameters   x   and   y   are not typed. trace(combine(4,5));                   // Displays: 9 trace(combine("hello ", "world"));     // Displays: hello world trace(combine(_root, "hello world"));  // Displays: _level0hello world 

While the preceding code may seem more convenient because of its flexibility, it actually causes more work and problems than a typed version would. When the method is not typed, the program code ”not the compiler ”must decide whether the method's return value is useful or nonsensical . The program becomes responsible for guarding against basic data errors rather than relying on the compiler to do so automatically. For example, if we wanted to be sure that the result of combine(4, 5) were a number, we'd have to use:

 if (typeof combine(4, 5) == "number") {   // Okay to proceed... } else {   trace("Unusable value returned by combine( )."); } 

The preceding code successfully safeguards against nonnumeric uses of combine( ) , but it takes five lines to do so! What's more, it is required every time the program uses the combine( ) method, and similar code would be needed for any other calls to methods without return types. That is, absent compile-time datatype checking, the programmer has to implement a lot of runtime datatype checking.

With datatypes declared, the combine( ) method would look like this:

 function combine (x:Number, y:Number):Number {   return x + y; } 

In which case, the following code:

 combine("ten", 5); 

would automatically generate a type mismatch error at compile time. With the typed version of the method, we can omit the earlier five lines of type checking code because the compiler checks the datatypes for us and displays an error when a type is misused.

3.5.1 Creating Parameters That Accept Multiple Types

Occasionally it's convenient to create a function or method whose parameters accept more than one type of data. For example, you might want our recent combine( ) method to work with both strings and numbers . Or you might want to be able to send both XML objects and strings to a server, as does the built-in XMLSocket class via its send( ) method. The XMLSocket . send( ) method's only parameter, data , can accept a String object, an XML object, or an item of any other datatype, allowing the developer to choose what's appropriate for a given situation. The following calls to XMLSocket.send( ) are both legal:

 theSocket.send("Hello World");       // Send a string. var doc:XML = new XML("<P>Hello World</P>"); theSocket.send(doc);                 // Send an XML instance. 

As we learned earlier under "Handling Any Datatype," we can use any type of data wherever the Object datatype is declared. Hence, we can make a parameter that accepts any type of data by specifying Object as the parameter's datatype. Again, this technique works because the Object class is the superclass of all ActionScript classes, so all types are compatible with the Object type.

The following function defines a parameter, msg , that can accept any type of data. Within the body of the function, we invoke toString( ) on whatever value was passed for msg :

 function output (msg:Object):Void {   trace(msg.toString( )); } 

The toString( ) method is defined by the Object class, so it is guaranteed to work on any value passed for msg .

But our earlier warning applies here: be careful not to overuse this technique, as it effectively disables the compiler's ability to generate type errors for the parameter in question.

To restore the compiler's ability to type check msg , we must cast the generic msg value to the desired datatype (we'll cover casting soon). For example, the following rewrite of the output( ) function checks the datatype of msg . If msg is a string, the function traces the text to the Output panel as-is. If msg is an instance of the XML class, the function assigns it to a new variable, doc , whose declared type is XML . Casting msg to the XML type using XML(msg) , tells the compiler to treat msg as an XML object. The function then uses doc instead of msg to access the data, thereby enjoying the benefits of type checking. Specifically, if we access non- XML methods and properties on doc , the compiler generates errors:

 function output (msg:Object):Void {   // Use   typeof   to detect the datatype of primitive values.   if (typeof msg == "string") {     trace(msg);   }   // Use   instanceof   to check the class of an object.   if (msg instanceof XML) {     var doc:XML = XML(msg);     trace(doc.firstChild.firstChild.nodeValue);   } } 

Some languages (e.g., Java) address the need for multiple parameter types with so-called method overloading . Method overloading lets multiple methods have the same name but define unique parameter types. When an overloaded method is called, the compiler determines which version of the method to execute based on the datatype(s) of the passed argument(s). ActionScript 2.0 does not support method overloading. However, we'll see how to simulate overloaded methods and constructor functions in Chapter 4.


3.5.2 Allowing Property and Method Creation on a Class's Instances

As of the introduction of ActionScript 2.0, the compiler normally generates an error when you attempt to access a nonexistent property or method on an object. (A nonexistent property or method is one that is not defined by the object's class.) If, however, you feel you must create a class that allows new properties or methods to be added to its instances at runtime, you can disable nonexistent property and method checking by declaring the class dynamic . For much more information, see "Class Attributes" in Chapter 4.

Do not take this technique lightly. Dynamic classes let you create instances that have unknown properties and methods, which can cause unexpected results in the rest of your program (or when incorporated into a different program).

3.5.3 Bypassing Type Checking on a Per-Use Basis

As we just learned, omitting the datatype for a data container disables type checking for all uses of that container. However, type checking may also be sidestepped for a single use of a data value in one of two ways.

First, we can use the [] operator to access methods and properties without concern for compile-time type checking. For example, the following code generates an error because the Sound class does not define the property url :

 var song:Sound = new Sound( ); song.url = "track3.mp3";  // Error! 

But if we use the [] operator when referring to url , no error occurs, and a new url property is added to the song object:

 var song:Sound = new Sound( ); song["url"] = "track3.mp3"; trace(song["url"]);         // Displays: track3.mp3 

This technique adds a new property (in this case, url ) to the object instance (in this case, song ), not to the class itself (in this case, Sound ). We still haven't fully addressed the question of how to add methods or properties to a class dynamically. We'll cover that in Chapter 6, under "Augmenting Built-in Classes and Objects." All we've done here is bypass some of the type checking otherwise performed by the ActionScript compiler.

Alternatively, we can cast the value to the desired type (casting is covered next , so if it's new to you, you may want to skip ahead for now). For example, the following code casts someValue to the TextField class so that it can be stored in the variable tf , whose type is TextField :

 var tf:TextField = TextField(someValue); 

In ActionScript 2.0, casting data to a datatype always succeeds at compile time. That is, the preceding code convinces the compiler that whatever the actual type of someValue , it should be treated as a legitimate TextField object. In reality, the cast operation may fail at runtime, but we're talking only about compile-time type checking here.

Once again, do not take either of these techniques for bypassing compile-time type checking lightly!

It's considered extremely bad form to add a new property (i.e., one not defined by the object's class) to a single object instance. It can also be dangerous to cast an object to a class if the object is not actually an instance of that class.


You may fool the compiler but, as we'll learn next, at runtime you can easily end up with a null reference instead of the object you thought you cast. In the vast majority of cases, you should not circumvent the type checking system. (However, as discussed in the next section, casting certainly has other valid uses.)

 <  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