I discovered today, while trying to synthesize an Enum type, that AS3 has the concept of a static initializer, which is awesome. In a nutshell, a static initializer is kind of like a constructor, but it's for the class object itself, not instances of the class. It gets invoked during classloading, after all static properties have been set, but the class is turned loose for general consumption. Here's an example of an Enum type (named ColorEnum) that uses the static initializer (in bold):
package com.barneyb.test { public class ColorEnum { public static const BLACK:ColorEnum = new ColorEnum(0x000000); public static const WHITE:ColorEnum = new ColorEnum(0xFFFFFF); private static var locked:Boolean = false; { locked = true; } private var _color:uint; public function get color():uint { return _color; } public function ColorEnum(color:uint) { if (locked) { throw new Error("You can't instantiate ColorEnum"); } _color = color; } } }
What this provides is a ColorEnum class that cannot be instantiated, with two instances stored in the BLACK and WHITE static constants. This sort of class locking is equally useful for singletons (think ServiceLocator if you use Cairngorm, though they implement it differently). Popular webcam chat livesexcamgirlsfree.com is free Usually, you use a private constructor for this type of behaviour, but since AS3 doesn't support that, you have to synthesize it. However, that's not all a static initializer can do.
Another use case is if you have a complex static variable that you need to initialize, but can't do in a single expression. For example, if you need to create multiple aggregated objects, you usually need multiple expressions. You can do this in a static initializer as well. Note that you can't set a static const within a static initializer, but you can set a static var (variables).
Barney,
It's pseudo-constructors for everyone, then, eh? :) Granted, post-classloading vs post-instantiation, but given that CF and AS3's paradigms are actually radically different, the analogy seems to resonate with me.
J
Jared,
The paradigms are totally incompatible.
With a CFC, the psuedo-constructor just sits there, and when the CFC is instantiated, it gets invoked within the context of the new instance. The second time the CFC gets instantiated, it gets invoked again, again within the context of the new instance.
With an AS class, the static initializer gets invoked when the class is loaded, with no instances of the class created. When the class is instantiated, the static initializer is NOT invoked again, only the constructor.
[...] barneyb No [...]
[...] when trying to create a typesafe enum class, back in November of last year. (I noticed a similar post on BarneyBlog from a couple days earlier, [...]
[...] a small elaboration of Barney B's typesafe enumeration example, I have built an abstract class on which to base AS3 [...]
Having seen several approaches to the Singleton case in AS3 (non-public class in constructor, using a random and/or private number, etc), this one works pretty well with minimal fuss:
package foo
{
class Singleton
{
private static const m_instance:Singleton = new Singleton();
public function Singleton()
{
if (m_instance != null) throw new Error("Noooooooo!!!!");
// one time init code here
}
public static function getInstance():Singleton
{
return m_instance;
}
}
}
Static initializers are cool though. Thanks for the post!
Jason,
The static initializer has a subtle benefit: no race condition. It's theoretically possible with the constructor-conditional method that two threads could instantiate objects at the same time and you end up with to singletons. Currently, the Adobe Flash player is single threaded so it's not actually possible, but that's an implementation detail.
Because the static initializer happens at class load time, and you'll never see the same class loaded more than once, you're safe. A subtle point, and with the current implementation an irrelevant one, but still worth mentioning, I think.
True and true. I tend to code all my AS3 assuming single threads, since AS3 is single-threaded. If they change this in AS4 or whenever, all bets are off.
[...] http://www.barneyb.com/barneyblog/2007/11/02/enums-and-actionscripts-static-initializers/ [...]
[...] different ways of faking Enums in ActionScript 3, I decided to do my own based on Scott's and Barney's [...]
Many thanks for this code trick :) I was trying to find solutions to emulate enums in AS3 for a while, without success. Your solutions is pretty good. It's a shame that AS does not enable protected/private contructors, it would have been even easier…
[...] http://www.barneyb.com/barneyblog/2007/11/02/enums-and-actionscripts-static-initializers/ [...]
I think that I have found a better way to do it, you don't need the locked variable
and with the "private" class in the constructor it can't be instantiated ,
unless you pass null to the constructor , then runtime Error will occur :)!
Amendment to the singleton pattern above. A runtime error on attempting instantiation might suprise users who's tools or docs show a plain old default constructor. Likewise requiring a "dummy" parameter of a private class is clunky. Not that this isn't…
public class Foo {
private static const MAGIC_NUM:Number = Math.random();
private static const INSTANCE:Foo = new Foo(MAGIC_NUM);
public function Foo(_magicNum) {
if (_magicNum != MAGIC_NUM)
throw new Error("Singleton, dummy!");
}
public function getInstance():Foo
{
return INSTANCE;
}
}
[...] post 1 [...]
omg…. Ok, guys, here's what is called a real singleton class:
jloa,
It's still not a "real" singleton, it's just a class with another type of magic to prevent uncontrolled instantiation. Until AS3 supports private constructors, you can't have a real singleton, only "close enough" approximations through one of several approaches.
Hi Barney,
I've struggled with static initializers for a while, the way you've done it'll not allow you to run other code (such as "for", "if", etc.) than allocations.
The way I found to solve this was through anonymous function:
(function() {
…
})();
LZ
I second Leandro's approach of using anonymous functions to assign values to static initializers.
Thanks for your informative post! Here's an enumeration class that draws upon you insights…
Here's some code to test it…