In the original case that started this thread, event.currentTarget.x
most likely fails because x
has a getter function, and the compiler doesn’t realize that due to the type of event.currentTarget
.
public var x(get, set):Float;
event.currentTarget
is typed as openfl.utils.Object
, which is an abstract, but it ultimately resolves to the Dynamic
core type. With Dynamic
, the Haxe compiler allows you to access arbitrary fields that may or may not exist without reporting any compile-time errors. It’s up to us developers to ensure that we’re doing things safely when using Dynamic
so that we don’t get run-time exceptions or other strange behaviors.
When a variable is typed as Dynamic
, the Haxe compiler has no idea that getter or setter functions should be used for certain properties. Basically, in the case of accessing event.currentTarget
, the Haxe compiler tries to access a simple variable field named x
instead of calling get_x()
, like it would if a variable were typed as DisplayObject
or Sprite
. It doesn’t matter that the variable typed as Dynamic
holds a Sprite
at run-time either because the compiler has no way of knowing that at compile-time.
To put things another way, if you have any experience using reflection in Haxe, it’s like calling Reflect.field()
instead of Reflect.getProperty()
. Reflect.getProperty()
first checks if a getter function exists, and then it falls back to a simple variable field, if it does not. Reflect.field()
goes directly for the simple variable field, and it does not check for a getter function at all.
That’s why casting is the solution here. When you use cast(event.currentTarget, DisplayObject).x
instead, you’re giving a hint to the Haxe compiler that event.currentTarget
is a DisplayObject
, and it can find the definition of the x
property that declares that a getter should be used.
All that being said, some targets will automatically detect when a getter or setter exists at run-time. The Haxe language doesn’t provide that ability. It’s just how those targets coincidentally work. So that’s why it works for some targets, but not others. It should simply be considered a quirk of the specific target, and not something that you can rely on for all targets.
It’s basically the same thing with event.currentTarget.startDrag()
. The Haxe compiler doesn’t know that the Dynamic
field event.currentTarget
actually holds a Sprite
, so it may not be able to find the method named startDrag()
reliably on all targets. cast(event.currentTarget, Sprite).startDrag()` is the way to go there too.
Whenever accessing event.target
or event.currentTarget
in an event listener, you should basically always cast to a more specific type. You can sometimes get away with not casting, but it has a risk that could have been easily avoided by casting every time. Additionally, you may actually be causing the Haxe compiler to generate less optimized code any time that you use Dynamic
without casting, which means that your project may run slower. None of us want that!