Use Interface to pass Class to function

I’m trying to port over some Flash stuff to haXe, and I’ve hit a wall…

I’ve call a method like so:

windowManager.openWindow( myWindow );

the function is defined:

public function openWindow( window : ISimpleWindow ) : ISimpleWindow

The window I’m trying to open is defined:

class myWindow extends BasicWindow

And:

class BasicWindow extends Sprite implements ISimpleWindow

But this fails saying myScreen should be ISimpleWindow, if I change that to the base class (Basic Window) it creates the same error, Sprite also…

Is it possible work with a Class by referring to its Base Class or Interface? Or, do I need to work this another way?

Does using something like Class<ISimpleWindow> work for the type?

ISimpleWindow would refer to a class instance, but the above (I think) will work for referring to class types directly :slight_smile:

Did you mean something like:

public function openWindow( window : Class<ISimpleWindow> ) :ISimpleWindow

It doesn’t appear to work - I’m unable to access methods of BasicWindow, even if they’re in the Interface.

And, further testing shows, that if I remove the type declaration, I can pass the Class to the function. But, if I then try to access any methods of the Class that fails

so:

	public function checkWindow(window):Void
	{
	trace("checkWindow");
	trace(window);

Will get me

.hx:71: checkWindow
.hx:72: [class MyScreen]

Which seems good.

But then if I try to do something like:

	public function checkWindow(window):Void
	{
	trace("checkWindow");
	trace(window);
	var w = new window();

That will throw a number of errors.

I do realise that I’ve drifted a bit from the original question, but, at the moment I’m trying to get anything working

Maybe Type.createInstance (window, []);?

http://api.haxe.org/Type.html#createInstance

You seem to be calling openWindow() twice: once for myWindow and a second time for myScreen. It’s the second one that’s causing the error, and you never mentioned what the MyScreen class extends.

Apologies that was a minor error - myScreen & myWindow are equivalent.

Type.createInstance (window, []);

Works, so I can dynamically instantiate a Class. But, getting back to the initial query…

Is there a way of passing a Class instance whilst only referring to it by either its Base Class or Interface?
What happens is that if a function returns Class< Base Class> or Class< Interface> it then means I can’t access methods of the Class instance.

I have tried returning Class< Dynamic> which I thought might work, but, sadly it didn’t…

Finally got this working.

Rather than trying to return Class< Dynamic> I just made the method return Dynamic. This then allows it to be passed & manipulated as it was in Flash.

I expect this solution would be considered rather inelegant - I’m open to better suggestions :slight_smile:

Do you mean:

public function openWindow<T> (window:Class<T>):T {


}

For generically handling any base class type?

…or perhaps:

public function openWindow<T:(Class<ISimpleWindow>)> (window:Class<T>):T {


}
1 Like

Ok - I’m trying to add this - can you explain what function< T> is/does? And, using T in general?

T inside of a greater/less than symbol identifies a generic type. That means that when you call a function like what @singmajesty placed, you would call it like this:

openWindow<BaseWindow> (myInheritedWindow);

That would return BaseWindow, since that’s what you passed in.

I’m really close to getting this 100% working now.

My solution has been to do this:

openWindow( windowClass : Class < BasicWindow > ) : BasicWindow

Which works… Obviously, I’ve used the base Class rather than the Interface.

But, I’ve just realised that it’s not quite right… I can access any methods from the base class, but those that are exclusive to the actual class being passed aren’t accessible.

Is there any way to return something like Object< BasicWindow> i.e an object with a base Class of BasicWindow, but not specifically that?

That’s what you’re already doing.

For instance, this would be perfectly valid, assuming FancyWindow extends BasicWindow:

public function openWindow() : BasicWindow {
    return new FancyWindow();
}

It’s really strange, if I try

var w1 = new FancyWindow();
trace(w1); // [object FancyWindow]

var w2 = openWindow(FancyWindow);
trace(w2); // [object FancyWindow]

which is what you’d expect.

But if I try:

var w1 = new FancyWindow();
w1.fancyFn();// works fine

var w2 = openWindow(FancyWindow);
w2.fancyFn();// Error, class BaseWindow has no field fancyFN

If I call a method of the base class (overridden or not), that will work.

< edit 1 - I’m not sure this worked in Flash, and can’t now check it>
< edit 2 - this is a very specific test case, which I don’t need - other than this it all works>

var w2 = openWindow(FancyWindow);
w2.fancyFn();// Error, class BaseWindow has no field fancyFN

This is expected, thanks to how type inference works.

Remember, openWindow() declares its return type as BaseWindow, even if it always returns FancyWindow. That means that when Haxe goes to infer the type of w2, it concludes it’s a BaseWindow.

Let’s simplify it a bit, by going back to w1.

var w1 = new FancyWindow();
w1.fancyFn();// works fine

That works fine because w1 has type FancyWindow. But what if it didn’t?

var w1 : BaseWindow = new FancyWindow();// works fine
w1.fancyFn();// Error, class BaseWindow has no field fancyFN

That’s right: even though it’s still a FancyWindow, if we tell the compiler to treat it as a BaseWindow, the compiler will act like it’s a BaseWindow.

Why would you do that? Because maybe you’ll want to change w1. Maybe you’ll want to replace it with a plain old BaseWindow, or maybe you want to use a different subclass. As long as it’s declared as a BaseWindow, you won’t run into any issues with this. (And by the way, that other subclass wouldn’t have a function named fancyFN(), so it’s a good thing the compiler lets you know.)


Now to fix the error. There are two things you can do here.

The safe option is to declare that openWindow() returns FancyWindow. Because if that’s the only thing it returns, why not?

public function openWindow() : FancyWindow {
    return new FancyWindow();
}

The less-safe option is to use a cast. If openWindow() returns something other than a FancyWindow, you’ll get a runtime error. Otherwise, it’ll work fine.

var w2 : FancyWindow = cast(openWindow(FancyWindow), FancyWindow);

The solution @singmajesty gave you is better. Example here http://try.haxe.org/#d5d13

Oh, now I get what Charlie was asking for. Not sure how I missed that.

Agreed, that’s a better solution.


As long as I’m explaining everything, I may as well explain this solution too.

public static function openWindow<T:BasicWindow>(cls:Class<T>):T {
    return Type.createInstance(cls, []);
}

First, this function takes a “type parameter,” making it a “generic” function. The type parameter (T) is declared in the angle brackets: <T:BasicWindow>. That means it has to be BasicWindow or a subclass thereof. (No need to use an interface.)

Each time you call the function, you set T to a specific type. How? It’s inferred from the arguments you pass. So when you call this…

openWindow(FancyWindow);

…you’re passing a value of type Class<FancyWindow>. Since the parameter is type Class<T>, Haxe infers that T is FancyWindow. This means that the function now returns type FancyWindow.

Now your job is to create an actual instance of T to return. You can’t write new T() because of reasons, but Type.createInstance(cls, []) uses reflection to do the same thing.

(Hopefully this post made sense.)

Wow, thank you all for all your efforts in helping me fix this, and getting me to understand it. (I am wondering if rather than blindly trying to port something old, I could have found a better way of working things.)

Just one query, working from the example @mrcdk gave.

If i wanted to do something like:

static function open<T:(IBasicWindow)>(cls:Class<T>):T {
        var w = Type.createInstance(cls, []);
        return doSomethingTo(w);
    }

static function doSomethingTo<???)>(instanceOfExtendedBasicWindow:???):??? {
        instanceOfExtendedBasicWindow.init();
        return instanceOfExtendedBasicWindow;
    }

How would I set that up?
I think that how I would refer to SubClasses, and instances thereof, has eluded me since I started looking at this (and also underlined how loosely Flash handled Types.)

I think a bit of an explanation of what an interface is and how it works, is in order.

An interface works as a kind of contract that tells you how you can communicate with a collaborator.
In this case you’re telling your openWindow method that it accepts a parameter of the type ISimpleWindow. This means that from openWindow point of view, your window object provides only the methods advertised by ISimpleWindow.
In the scope of openWindow, the fact that window is an instance of myWindow or that it extends BasicWindow is unknown. This is what an interface is all about, and this is how it works in every OO language that I know of, and believe me, that’s a lot of them.

You can still make use of the methods that exist in your object but are not declared on the interface by casting it to the myWindow class. This will work if your instance of ISimpleWindow is a myWindow but will cause a runtime error if it happens to be some other implementation of ISimpleWindow. This is also an indicator of some architecture problem with the code, as it expects to get an ISimpleWindow but then uses methods that are not part of ISimpleWindow.

Cheers,
Marco Lopes

Thanks @mlopes, I appreciate you taking the time to add in an explanation.

The issue here isn’t understanding Interfaces. The issue is trying to convert something from Flash, which uses Interfaces as a “lazy” way of being able to pass both Classes and instances of those Classes between functions.
This isn’t possible in haXe, as it turns out, because it is much stricter about types.