Type checking can occur at either compile
time or run time. Statically typed languages, such as C++ and Java,
do type checking at compile time. Dynamically typed languages, such
as Smalltalk and Python, handle type checking at run time. As a
dynamically typed language, ActionScript 3.0 has run-time type checking,
but also supports compile-time type checking with a special compiler mode
called
strict mode
. In strict mode, type checking occurs at both compile time
and run time, but in standard mode, type checking occurs only at
run time.
Dynamically typed languages offer tremendous flexibility when
you structure your code, but at the cost of allowing type errors
to manifest at run time. Statically typed languages report type
errors at compile time, but at the cost of requiring that type information
be known at compile time.
Compile-time type checking
Compile-time
type checking is often favored in larger projects because as the size
of a project grows, data type flexibility usually becomes less important
than catching type errors as early as possible. This is why, by
default, the ActionScript compiler in Adobe Flash CS4 Professional
and Adobe Flex Builder is set to run in strict mode.
In order to provide compile-time type
checking, the compiler needs to know the data type information for
the variables or expressions in your code. To explicitly declare
a data type for a variable, add the colon operator (
:
) followed by the data type as a suffix to the variable
name. To associate a data type with a parameter, use the colon operator
followed by the data type. For example, the following code adds
data type information to the
xParam
parameter, and declares a variable
myParam
with an explicit data type:
function runtimeTest(xParam:String)
{
trace(xParam);
}
var myParam:String = "hello";
runtimeTest(myParam);
In
strict mode, the ActionScript compiler reports type mismatches as
compiler errors. For example, the following code declares a function
parameter
xParam
, of type Object, but later attempts to assign values of
type String and Number to that parameter. This produces a compiler
error in strict mode.
function dynamicTest(xParam:Object)
{
if (xParam is String)
{
var myStr:String = xParam; // compiler error in strict mode
trace("String: " + myStr);
}
else if (xParam is Number)
{
var myNum:Number = xParam; // compiler error in strict mode
trace("Number: " + myNum);
}
}
Even in strict mode,
however, you can selectively opt of out compile-time type checking
by leaving the right side of an assignment statement untyped. You
can mark a variable or expression as untyped by either omitting
a type annotation, or using the special asterisk (
*
) type annotation. For example, if the
xParam
parameter in the previous example is modified so that
it no longer has a type annotation, the code will compile in strict
mode:
function dynamicTest(xParam)
{
if (xParam is String)
{
var myStr:String = xParam;
trace("String: " + myStr);
}
else if (xParam is Number)
{
var myNum:Number = xParam;
trace("Number: " + myNum);
}
}
dynamicTest(100)
dynamicTest("one hundred");
Run-time type checking
Run-time type checking occurs
in ActionScript 3.0 whether you compile in strict mode or standard
mode. Consider a situation in which the value 3 is passed as an argument
to a function that expects an array. In strict mode, the compiler
will generate an error, because the value 3 is not compatible with
the data type Array. If you disable strict mode, and run in standard
mode, the compiler does not complain about the type mismatch, but
run-time type checking by Flash Player and Adobe AIR results in
a run-time error.
The
following example shows a function named
typeTest()
that expects an Array argument but is passed a value of
3. This causes a run-time error in standard mode, because the value
3 is not a member of the parameter’s declared data type (Array).
function typeTest(xParam:Array)
{
trace(xParam);
}
var myNum:Number = 3;
typeTest(myNum);
// run-time error in ActionScript 3.0 standard mode
There may also be situations where you get a run-time
type error even when you are operating in strict mode. This is possible
if you use strict mode, but opt out of compile-time type checking
by using an untyped variable. When you use an untyped variable,
you are not eliminating type checking but rather deferring it until
run time. For example, if the
myNum
variable in the previous example does not have a declared
data type, the compiler cannot detect the type mismatch, but Flash
Player and Adobe AIR will generate a run-time error because it compares
the run-time value of
myNum
, which is set to 3 as a result of the assignment statement,
with the type of
xParam
, which is set to the Array data type.
function typeTest(xParam:Array)
{
trace(xParam);
}
var myNum = 3;
typeTest(myNum);
// run-time error in ActionScript 3.0
Run-time
type checking also allows more flexible use of inheritance than
does compile-time checking. By deferring type checking to run time,
standard mode allows you to reference properties of a subclass even
if you
upcast
. An upcast occurs when you use a base class to declare the
type of a class instance but use a subclass to instantiate it. For
example, you can create a class named ClassBase that can be extended
(classes with the
final
attribute cannot be extended):
class ClassBase
{
}
You can subsequently create a subclass of ClassBase
named ClassExtender, which has one property named
someString
, as follows:
class ClassExtender extends ClassBase
{
var someString:String;
}
Using both classes, you can create a class instance
that is declared using the ClassBase data type, but instantiated
using the ClassExtender constructor. An upcast is considered a safe
operation, because the base class does not contain any properties
or methods that are not in the subclass.
var myClass:ClassBase = new ClassExtender();
A subclass, however, does contain properties or methods
that its base class does not. For example, the ClassExtender class
contains the
someString
property, which does not exist in the ClassBase class.
In ActionScript 3.0 standard mode, you can reference this property
using the
myClass
instance without generating a compile-time error, as shown
in the following example:
var myClass:ClassBase = new ClassExtender();
myClass.someString = "hello";
// no error in ActionScript 3.0 standard mode
The is operator
The
is
operator, which is new for ActionScript 3.0, allows you
to test whether a variable or expression is a member of a given
data type. In previous versions of ActionScript, the
instanceof
operator provided this functionality, but in ActionScript
3.0 the
instanceof
operator should not be used to test for data type membership.
The
is
operator should be used instead of the
instanceof
operator for manual type checking, because the expression
x instanceof y
merely checks the prototype chain of
x
for the existence of
y
(and in ActionScript 3.0, the prototype chain does not
provide a complete picture of the inheritance hierarchy).
The
is
operator examines the proper inheritance hierarchy and
can be used to check not only whether an object is an instance of
a particular class, but also whether an object is an instance of
a class that implements a particular interface. The following example
creates an instance of the Sprite class named
mySprite
and uses the
is
operator to test whether
mySprite
is an instance of the Sprite and DisplayObject classes,
and whether it implements the IEventDispatcher interface:
var mySprite:Sprite = new Sprite();
trace(mySprite is Sprite); // true
trace(mySprite is DisplayObject);// true
trace(mySprite is IEventDispatcher); // true
The
is
operator checks the inheritance hierarchy and properly
reports that
mySprite
is compatible with the Sprite and DisplayObject classes
(the Sprite class is a subclass of the DisplayObject class). The
is
operator also checks whether
mySprite
inherits from any classes that implement the IEventDispatcher
interface. Because the Sprite class inherits from the EventDispatcher class,
which implements the IEventDispatcher interface, the
is
operator correctly reports that
mySprite
implements the same interface.
The following example shows the same tests
from the previous example, but with
instanceof
instead of the
is
operator. The
instanceof
operator correctly identifies that
mySprite
is an instance of Sprite or DisplayObject, but it returns
false
when used to test whether
mySprite
implements the IEventDispatcher interface.
trace(mySprite instanceof Sprite); // true
trace(mySprite instanceof DisplayObject);// true
trace(mySprite instanceof IEventDispatcher); // false
The as operator
The
as
operator, which is new in ActionScript 3.0, also allows
you to check whether an expression is a member of a given data type.
Unlike the
is
operator, however, the
as
operator does not return a Boolean value. Rather, the
as
operator returns the value of the expression instead of
true
, and
null
instead of
false
. The following example shows the results of using the
as
operator instead of the
is
operator in the simple case of checking whether a Sprite instance
is a member of the DisplayObject, IEventDispatcher, and Number data types.
var mySprite:Sprite = new Sprite();
trace(mySprite as Sprite); // [object Sprite]
trace(mySprite as DisplayObject); // [object Sprite]
trace(mySprite as IEventDispatcher); // [object Sprite]
trace(mySprite as Number); // null
When you use the
as
operator, the operand on the right must be a data type.
An attempt to use an expression other than a data type as the operand
on the right will result in an error.