Indirect function references - function

Shouldn't this (or something like it) work? and/or How can I accomplish this some other way?
class A {
void init() {initializeSomething()}
}
main() {
var f = A.init;
A a = new A();
a.f();
}
I want, in general, to store a reference to an instance method
somewhere, then call it somewhere else (something I can do in most other
languages, including JavaScript). I thought functions were first class
in Dart... and aren't methods functions? Are methods not first class?
It works as long as the method is static (which is of no use in my case) but not for instance methods...

Functions and methdods are different. You can only call methods on an instance of a class that has this method.
In your example (var f = A.init) you reference an instance method like a static (class) method.
What works is:
make init static
class A {
static void init() => initializeSomething();
// or
// static void init() {
// initializeSomething();
// }
}
main() {
var f = A.init;
A a = new A();
a.f();
}
or use a reference to init() of an actual instance of A:
class A {
void init() => initializeSomething();
}
main() {
A a = new A();
var f = a.init;
a.f();
}

Related

Saving static instance of Main - Actionscript 3

So I need to access information from my document(Main.as) class.
I tried making this easy by saving a static instance of my Main class.
private static var _instance:Main;
public static function get instance():Main { return _instance; }
public function Main() {
_instance = this;
}
Then when in another class I try to use this I get a null reference error.
public function InputController():void {
main = Main.instance;
main.stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyPress);
main.stage.addEventListener(KeyboardEvent.KEY_UP, OnKeyRelease);
}
I get an error on the main.stage.addEventListener lines.
If you have timeline coding, you can instantiate your static variable with _instance=this (should be accessible, as main timeline is a part of document class) at the first keyframe, and reference there from elsewhere via property.
You are trying to return an instance without instantiating it. Use the following code for instantiating the class first.
private static var _instance:Main;
public static function get instance():Main
{
if(_instance == null)
_instance = new Main();
return _instance;
}

Referencing own constructor in inner function

Here's a toy example distilled from a complex class:
public class MyClass {
public function MyClass() {
trace('Created');
}
public static function makeObjectAsync(callback:Function):void {
inner();
function inner():void {
var object:MyClass = new MyClass(); // line 10
callback(object);
}
}
}
After calling the static function:
MyClass.makeObjectAsync(function(object:Myclass):void { ... })
the following run-time exception occurs at line 10:
TypeError: Error #1007: Instantiation attempted on a non-constructor.
Why is this, and what can I do about it?
Edit
It appears that new (MyClass)() works. Now I'm possibly more confused.
Not too clear on the WHY to be honnest. It has to do with the scope inherited by anonymous functions, depending on how they are declared.
I have 2 solutions for you though.
If your makeObject method was not static, it would work.
Declare your anonymous function the other way :
public static function makeObjectAsync(callback:Function):void {
var inner : Function = function():void {
var object:MyClass = new MyClass();
callback(object);
};
inner();
}
You shouldn't call your variable "object". Why do you nested your inner function? Why don't you just:
public static function makeObjectAsync(callback:Function):void {
callback(new MyClass());
}
Or if you really want that nested function:
public static function makeObjectAsync(callback:Function):void {
inner();
function inner():void {
callback(new MyClass());
}
}
And you can't recall the class' constructor again, use a function which is called in the constructor then call it again. With this you aren't referencing the constructor but creating a new instance of the class.

AS3 syntax to call a method of another class by its name in a variable? (Chained callbacks)

Object A calls method M of object B, passing it two callbacks for two cases: cbYes and cbNo.
B, in turn, performs a web service async call, creating Object C (api) instance with the only callback: method N of B. This callback will decide which of the two callbacks to call.
I store cbYes and cbNo functions as B's private vars of type Function.
How can I call either callback? They're not children of B, so syntax B[cbYes](); is not the way. Unreal code example:
class A {
public function Smth() {
var instB:B = new B( cbYes, cbNo);
}
public function cbYes( e:Event) { doSomething(); }
public function cbNo( e:Event) { doSomething(); }
}
class B {
private var _cb1:Function;
private var _cb2:Function;
public function B( cb1, cb2) {
_cb1 = cb1; _cb2 = cb2;
var worker:C = new C();
C.apiMethod123( cbAfterCall);
}
public function cbAfterCall( Result:*) {
if( Result = 1) {
// here I need to call callback from _cb1
} else {
// here I need to call callback from _cb2
}
}
}
class C {
private var _Callback:Function;
public function C() { }
public function apiMethod123( cb:Function) {
this._Callback = cb;
// create a URLLoader or a Loader and do a web service call
}
public function urlCallback( e:Event) {
// parse response
this._Callback();
}
}
Ok, while I was putting together this sample code, I realised I already solved this with the api caller worker! :-) Got to have more sleep.
AfterQuestion: does this architectural approach seems really wrong? Please advice a better one, or a pattern that suits the system where concurrent asynchronous API calls are used.
If you store cbYes and cbNo as member variables inside B. You can call them like normal functions:
public function foo(cbYes:Function,cbNo:Function):void
{
this.cbYes = cbYes;
this.cbNo = cbNo;
}
public function bar():void
{
cbYes();
}
Instead of using callbacks like this you can use the event system to achieve the same result. You can create custom events and dispatch the yes/no event.

Overriding function from another class

I am defining this function in one of my classes:
public function onUse():void {};
Then in another of my classes (let's call it "class2"), I create a object of this class, and then want to override this function with another one. After some Google-Fu, I have found this, and used it...
button.onUse {
variable = value;
}
...but it executes instantly, and not when onUse() is called - which seems to be an empty function, always.
I didn't find anything more than that - I tried a few things myself, like specifying a function inside class2 and using button.onUse = function();, but it always throws errors.
Can anyone tell me whether what I am trying to do is actually possible, and if it is, how can I do it?
You can only override functions when you are extending the class:
public class A {
public function foo():void {
doStuff();
}
}
public class B extends A {
override public function foo():void {
doOtherStuff();
}
}
var n:A = new A();
n.foo(); // => calls doStuff();
var o:B = new B();
o.foo(); // => calls doOtherStuff();
Hence, assigning a different function to a class method of an instance is not possible at runtime.
You can, however, let your original class contain a field of type Function, and then simply assign a different closure to it.
public class A {
public var foo:Function;
}
var n:A = new A();
n.foo = function ():void {
doStuff();
};
n.foo(); // => calls doStuff();
var o:A = new A();
o.foo = function ():void {
doOtherStuff();
}
o.foo(); // => calls doOtherStuff();
check the syntax of
button.onUse {
variable = value;
}
a function would be defined as
public function onUse():void {};
and overwritten with
override public function onUse():void {
}
in a different class
the way you're trying to do it, does not constitute overriding a function.
What I've done in similar circumstances is create a onClickFunction function in the class
public var onClickFunction:Function = null;
and then in the CLICK event listener function add
if(onClickFunction != null){
onClickFunction();
}
then you can assign your on-click functionality by doing something like this
button.onClickFunction = function():void{
variable = value;
// dostuff
}
this is not the best way of doing it, but probably the easiest way of implementing the functionality. And ideally you'd use inheritance the way the spacepirate suggested.

Does AS3 provide any way to stop a returned object being modified?

If an AS3 method returns a reference to a complex type, is there any way to make that 'readonly', like how you can have const member functions in C++? An architecture I want to use calls for a class building itself from a passed template object... and really the template object should not be modifiable. I'm currently forced to add call-back enumerators and/or lots of extra accessor methods.
Flex has an ObjectUtil.clone() method that will make a deep copy. The copy will still by modifiable, but since it's a copy, the changes won't propagate back to the original.
The method is no complicated so if you're not using Flex, just add this to a util class:
public static function copy(value:Object):Object
{
var buffer:ByteArray = new ByteArray();
buffer.writeObject(value);
buffer.position = 0;
var result:Object = buffer.readObject();
return result;
}
There is no way to do that in AS3, there is Sam's way of doing it, but it still requires copying that object before you return it, depending on the complexity of that object, it can impact the performance.
Immutable interfaces are a near-equivillant to const-correctness. Here's an example:
interface CPoint {
function get x():Number;
function get y():Number;
}
class Point implements CPoint {
private var _x:Number;
private var _y:Number;
public function get x():Number { return _x; }
public function get y():Number { return _y; }
public function set x(val:Number) { _x = val; }
public function set y(val:Number) { _y = val; }
public function normalize():void {
var length:Number = Math.sqrt(_x*_x + _y*_y);
_x /= length;
_y /= length;
}
public function Point(x:Number, y:Number) {
_x = x; _y = y;
}
}
If you return a Point as a CPoint reference, then its fields cannot be altered. You can do an explicit cast to a Point from a CPoint to force access, but you can do the same thing with const casting in C++.
Unfortunately, AS3 doesn't support covariance like it should, so things get unnecessarily difficult for const sub-objects. For example, if you had a Line class that was made up of two points, you might want to say line.start.x = 47; if you have full access to the line, but allow reading of line.start.x through an immutable interface. You could do this if there was covariance, but instead you'll need to add separate get properties for mutable and immutable properties. So, you'd end up instead with line.cstart.x for reads from a CLine. Something like this:
interface CLine {
function get cstart():CPoint;
function get cend():CPoint;
}
class Line implements CLine {
private var _end:Point;
private var _start:Point;
public function get cend():CPoint { return _end; }
public function get cstart():CPoint { return _start; }
public function get end():Point { return _end; }
public function get start():Point { return _start; }
public function Line(x1:Number, y1:Number, x2:Number, y2:Number) {
_start = new Point(x1, y1);
_end = new Point(x2, y2);
}
}
I would create a flash.utils.proxy object. You could create a proxy object that has read only implementation of a child that is passed in.
Here is the documentation for creating a proxy object. http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/utils/Proxy.html
Note: Proxy is pretty damn slow, since you'll be bypassing native object checking, and replacing it with a function call -- which when using a lot will be slow. I would do some simple performance testing first.
note: This is pseudo-code.
use namespace flash_proxy;
dynamic class ReadOnly extends flash.utils.Proxy {
private var target:Object;
public function ReadOnly(target:Object) {
this.target = target;
}
flash_proxy function getProperty(name:*):*
return target[name];
}
flash_proxy function setProperty(name:*, value:*):void
// throw an error or do nothing
}
}
You could then do:
var readOnly:ReadOnly = new ReadOnly(stage.loaderInfo.parameters);
readOnly.someparameter = 'newvalue';
trace(readOnly.someparameter); // should be old value