I’ve talked before about explicit type conversion and used the function-call style (Type(obj)) and the as keyword to accomplish the task. Today, I’m going to talk about implicit type conversion and use—as implicit would imply—no operators at all!

There are a limited set of circumstances where AS3 doesn’t require you to explicitly convert types. You simply assign a value of one type to a variable of another type like so:

function foo(numberVal:Number): void
{
	var intVal:int = numberVal; // Number -> int
}

This type conversion can be done even when you’ve strongly typed, like in the above example. It does not require you to use the * type or anything *-typed like the fields of an Object, Array, or Dictionary objects. Those strongly-typed situations are what today’s article is all about, so let’s look at them:

int uint Number Boolean
int=… YES YES YES NO (error)
uint=… YES YES YES NO (error)
Number=… YES YES YES NO (error)
Boolean=… YES (warning) YES (warning) YES (warning) YES

As you can see, type conversion between the basic types is often permissible. The exceptions are that you can’t implicitly convert a Boolean to an int, uint, or Number as you get a compiler error in MXMLC 4.1:

Error: Implicit coercion of a value of type Boolean to an unrelated type int.
Error: Implicit coercion of a value of type Boolean to an unrelated type uint.
Error: Implicit coercion of a value of type Boolean to an unrelated type Number.

You can, however, do the reverse and implicitly convert an int, uint, or Number to a Boolean, but you get a compiler warning:

Warning: int used where a Boolean value was expected.  The expression will be type coerced to Boolean.
Warning: uint used where a Boolean value was expected.  The expression will be type coerced to Boolean.
Warning: Number used where a Boolean value was expected.  The expression will be type coerced to Boolean.

Ignoring those warnings, I made up a little performance test to check the speed of these implicit conversions:

package
{
	import flash.display.*;
	import flash.utils.*;
	import flash.text.*;
 
	public class ImplicitTypeConversion extends Sprite
	{
		private var __logger:TextField = new TextField();
		private function log(msg:*): void { __logger.appendText(msg + "\n"); }
 
		public function ImplicitTypeConversion()
		{
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			const REPS:int = 100000000;
			var i:int;
			var beforeTime:int;
			var afterTime:int;
			var loopTime:int;
			var intVal:int = 1;
			var uintVal:uint = 1;
			var numberVal:Number = 1;
			var booleanVal:Boolean = true;
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
			}
			afterTime = getTimer();
			loopTime = afterTime - beforeTime;
 
			log("int=...");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				intVal = intVal;
			}
			afterTime = getTimer();
			log("\tint: " + ((afterTime-beforeTime)-loopTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				intVal = uintVal;
			}
			afterTime = getTimer();
			log("\tuint: " + ((afterTime-beforeTime)-loopTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				intVal = numberVal;
			}
			afterTime = getTimer();
			log("\tNumber: " + ((afterTime-beforeTime)-loopTime));
 
			log("\tBoolean: n/a");
 
			log("uint=...");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				uintVal = intVal;
			}
			afterTime = getTimer();
			log("\tint: " + ((afterTime-beforeTime)-loopTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				uintVal = uintVal;
			}
			afterTime = getTimer();
			log("\tuint: " + ((afterTime-beforeTime)-loopTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				uintVal = numberVal;
			}
			afterTime = getTimer();
			log("\tNumber: " + ((afterTime-beforeTime)-loopTime));
 
			log("\tBoolean: n/a");
 
			log("Number=...");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				numberVal = intVal;
			}
			afterTime = getTimer();
			log("\tint: " + ((afterTime-beforeTime)-loopTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				numberVal = uintVal;
			}
			afterTime = getTimer();
			log("\tuint: " + ((afterTime-beforeTime)-loopTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				numberVal = numberVal;
			}
			afterTime = getTimer();
			log("\tNumber: " + ((afterTime-beforeTime)-loopTime));
 
			log("\tBoolean: n/a");
 
			log("Boolean=...");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				booleanVal = intVal;
			}
			afterTime = getTimer();
			log("\tint: " + ((afterTime-beforeTime)-loopTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				booleanVal = uintVal;
			}
			afterTime = getTimer();
			log("\tuint: " + ((afterTime-beforeTime)-loopTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				booleanVal = numberVal;
			}
			afterTime = getTimer();
			log("\tnumber: " + ((afterTime-beforeTime)-loopTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				booleanVal = booleanVal;
			}
			afterTime = getTimer();
			log("\tBoolean: " + ((afterTime-beforeTime)-loopTime));
		}
	}
}

One new feature of this performance test is that I’m subtracting out the time an empty loop takes. This is especially important as the actual body of the loop is so light that the time spent just doing the loop is a very significant chunk of the total. With the overhead gone, we can see much more clearly how much time is actually spent doing the type conversion. The only downside is that we can get negative times when the type conversion is nearly instantaneous. I’ll just list those as zeroes in the performance results below, but in actuality they were sometimes slightly negative or slightly positive. The following is for Flash Player 10.1.102.64 running on a 2.4 Ghz Intel Core i5 with Mac OS X 10.6:

int uint Number Boolean
int=… 0 0 265 n/a
uint=… 0 0 264 n/a
Number=… 0 101 0 n/a
Boolean=… 24 26 205 0

There were many zeroes in the above test, again indicating that the type conversion is nearly instantaneous. For some conversions though, this is clearly not the case:

  • Number to int or uint is very costly as the operation is inherently expensive. Conversion to Boolean is faster as there are only two possible values.
  • While converting int to Number is fast, converting uint to Number is mysteriously not fast at all.
  • Converting int and uint to Boolean is not quite free. It takes a little bit of time, but it’s quite unlikely to be the source of any real-world problems.

The above performance results table should serve as a good reference. Beware any conversion from Number in performance-critical code. Since it takes no typing at all to do the conversion, this could be a silent performance killer.