Haxe has a very powerful reflection API, allowing you to create objects of a class, which you don’t know until runtime.
var u:User = Type.createInstance(User, [“jason”,”mypass”]);
u.save();
Sometimes, you have a method where you want to create a class, but which class is created is specified by the user:
function createUser( cls:Class<User>, u:String, p:String ) {
var u:User = Type.createInstance( cls, [u,p]);
u.save();}
createUser( StaffMember, “jack”, “mypass” );
createUser( Subscriber, “aaron”, “mypass” );
So far so good. But what if we have a 3rd user class, “Moderator”, that actually has a constructor that requires 3 arguments, not just the username and password.
createUser( Moderator, “bernadette”, “mypass” );
This compiles okay, but will fail at runtime – it tries to call the constructor for Moderator with 2 arguments, but 3 are required.
My first thought was, can we use an interface and specify the constructor:
interface IUser {
public function new( user:String, pass:String ):Void
}
Sadly in Haxe, an interface cannot define the constructor. I’m pretty sure the reason for this is to avoid you creating an object with no idea which implementation you are using. Now that would work for reflection, but wouldn’t make sense for normal object oriented programming:
function createUser( cls:Class<IUser>, u:String, p:String ) {
var u:IUser = new cls(u,p); // What implementation does this use?
}
So it can’t be interfaces… what does work? Typedefs:
typedef ConstructableUser = {
function new( u:String, p:String ):Void,
function save():Void
}
And then we can use it like so:
function createUser( cls:Class<ConstructableUser>, u:String, p:String ) {
var u:ConstructableUser = Type.createInstance( cls, [u,p]);
u.save();}
createUser( StaffMember, “jack”, “mypass” );
createUser( Subscriber, “aaron”, “mypass” );
createUser( Moderator, “bernadette”, “mypass” ); // ERROR – Moderator should be ConstructableUser
In honesty I was surprised that “Class<SomeTypedef>” worked, but I’m glad it does. It provides a good mix of compile time safety and runtime reflection. Go Haxe!