The const and final keywords only apply at compile time. Despite having written about const and final before, readers frequently ask me about these two keywords. Today’s article will answer the question and definitively show that these keywords only apply at compile time: not runtime. UPDATE: const is still just a variable as far as performance goes, but its protections do extend to runtime.

Let’s start with const. Consider this simple class:

class MathConstants
{
    public static const TWOPI:Number = 6.28;
}

This is an extremely common sight in AS3 code, but it is secretly vulnerable. While the variable may be specified with the const keyword, it is only constant at compile time so this code will cause a compiler error:

MathConstants.TWOPI = 7;

However, you can actually overwrite the variable dynamically at runtime by accessing it in a way that the compiler doesn’t check. Consider this:

MathConstants["TWOPI"] = 7;

This code will execute just fine and effectively redefine the meaning of for anyone using the constant. The implication of this is that the “constness” of the variable is not preserved in the SWF that is produced. The checking for overwriting of the variable is only done at compile time, not runtime.

Now for the final keyword. AS3 allows both classes and functions to be declared final and both have similar meaning. Classes that are final cannot be extended by any other class. Functions that are final cannot be overridden by any deriving class. These are both compile-time only checks because classes and functions cannot be dynamically generated at runtime. The final keyword for classes does survive compilation as we see in flash.utils.describeType:

<type name="::FinalClass" base="Class" isDynamic="true" isFinal="true" isStatic="true">
  <extendsClass type="Class"/>
  <extendsClass type="Object"/>
  <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/>
  <factory type="::FinalClass">
    <extendsClass type="Object"/>
    <method name="finalFunc" declaredBy="::FinalClass" returnType="void"/>
    <method name="notFinalFunc" declaredBy="::FinalClass" returnType="void"/>
  </factory>
</type>
<type name="::NotFinalClass" base="Class" isDynamic="true" isFinal="true" isStatic="true">
  <extendsClass type="Class"/>
  <extendsClass type="Object"/>
  <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/>
  <factory type="::NotFinalClass">
    <extendsClass type="Object"/>
    <method name="finalFunc" declaredBy="::NotFinalClass" returnType="void"/>
    <method name="notFinalFunc" declaredBy="::NotFinalClass" returnType="void"/>
  </factory>
</type>

Notice that the final keyword for functions does not survive compilation, so we would expect it to do nothing. To see that there really isn’t any performance advantage to using final for classes or functions, consider a small test:

package
{
	import flash.display.Sprite;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.getTimer;
 
	public class CompileTimeOnly extends Sprite
	{
		private var logger:TextField = new TextField();
		private function row(...cols): void { logger.appendText(cols.join(",")+"\n"); }
 
		public function CompileTimeOnly()
		{
			logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(logger);
 
			var REPS:int = 100000000;
			var beforeTime:int;
			var afterTime:int;
			var i:int;
			var finalClass:FinalClass = new FinalClass();
			var nonFinalClass:NotFinalClass = new NotFinalClass();
 
			row("Function", "Time");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				finalClass.finalFunc();
			}
			afterTime = getTimer();
			row("FinalClass.finalFunc", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				finalClass.notFinalFunc();
			}
			afterTime = getTimer();
			row("FinalClass.notFinalFunc", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				nonFinalClass.finalFunc();
			}
			afterTime = getTimer();
			row("NotFinalClass.finalFunc", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				nonFinalClass.notFinalFunc();
			}
			afterTime = getTimer();
			row("NotFinalClass.notFinalFunc", (afterTime-beforeTime));
		}
	}
}
final class FinalClass
{
	final public function finalFunc(): void {}
	public function notFinalFunc(): void {}
}
class NotFinalClass
{
	final public function finalFunc(): void {}
	public function notFinalFunc(): void {}
}

Here we have two classes—FinalClass and NotFinalClass—each with two functions—finalFunc and notFinalFunc—so that we can check every permutation of final/not final.

I ran this test on the following environment:

  • Flex SDK (MXMLC) 4.5.1.21328, compiling in release mode (no debugging or verbose stack traces)
  • Release version of Flash Player 11.1.102.63
  • 2.4 Ghz Intel Core i5
  • Mac OS X 10.7.3

And got these results:

Function Time
FinalClass.finalFunc 650
FinalClass.notFinalFunc 642
NotFinalClass.finalFunc 637
NotFinalClass.notFinalFunc 650

Yes, there are variations in the above numbers. No, they are not statistically-relevant variations. In several runs of the test you will see the values fluctuate a little- sometimes higher and sometimes lower. There is no discernable pattern to the fluctuations and the the differences between the slowest and fastest function is typically within 20ms which, for 100000000 iterations of the loop, is essentially zero time.

So, when looking for ways to improve the performance of your AS3 app, don’t waste your time on the const and final keywords. They may help you improve your code by preventing simple coding mistakes, but they won’t help your runtime performance.

Questions? Comments? Suggestions? Spot a bug? Post a comment!