Compile Time Only
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 2π
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!
#1 by Rackdoll on March 19th, 2012 ·
cool!
That const work around is pretty nasty.
I mean why then implement the const keyword if its still a sorta var.
Nice work tho!
Keep on the greatness!
#2 by jackson on March 19th, 2012 ·
Well it’s still a nice compile-time check to prevent most accidents where constants would otherwise be overridden. There’s no real downside, so you might as well use them where appropriate.
#3 by aquahawk on March 20th, 2012 ·
may be here http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9b.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7f6d
This description is little bit incorrect. We can change instance constants more then one time in constructor.
We can do some magic and change const in runtime anywhere.
Prints
ConstTestBase constructed
ConstTest constructed, param: 15
0
10
20
http://dl.dropbox.com/u/12212546/test.zip FD project.
#4 by aquahawk on March 19th, 2012 ·
I have some errors in runtime while testing this.
In this case
error apperas
1074 Illegal write to read-only property.
But in this case
we have no error because its documented behavior. In constructor we can change instance constants.
In this case we will have 1074? because we trying to change instance constant in non constructor method.
#5 by jackson on March 19th, 2012 ·
Wow, I had never heard that constructors can change instance constants. Can you point me to the documentation on this?
I’ve removed the section about
const
from the article. Thank you very much for pointing this out!#6 by Zach on March 19th, 2012 ·
Nice article. Thanks for the definitive answer, or should i say “final answer” on the final keyword in AS3.
#7 by skyboy on March 19th, 2012 ·
As an unrelated note: functions without a body are marked as (literally, there’s a Boolean for this) trivial in the runtime so their execution can be skipped. If you add some complex (i.e., non-trivial) but fast behavior to the function, you’ll see a dramatic change in performance. No benefit is gained from final in either case; as far as performance is concerned, though final can still be useful to prevent subclasses from overriding the function.
#8 by Scott Enders on March 19th, 2012 ·
Very nice article. I wondered if the final keyword actually made a difference performance-wise. It is one of the keywords I don’t ever use. Thanks
#9 by Marcus Stade on March 19th, 2012 ·
I use the final keyword all the time. I don’t like the idea of subclasses being able to change the behavior of methods or properties, it violates the SOLID principles of object oriented design. I usually make my classes final as well, to promote composition over inheritance. If its not final, it’s usually an abstract like class that is internal to a package, implementing common functionality to implementations in that package. The methods would still be final however. I found these strategies leed to stronger designs, without requiring any extra work really. It leads to many smaller classes that are more since purposed however, which is arguably not a bad thing. (SRP FTW!)
#10 by jackson on March 19th, 2012 ·
These are all good reasons to use
final
. The article only addresses the problem of runtime performance, not class design. If you like usingfinal
, that’s fine. If you’re expecting it to improve performance, that’s not. :)