Default caller value for AS3 method - actionscript-3

Is there a way of setting the default value of a method scope parameter to be the caller?
In AS3 you can set default values for method parameters like so:
function myFuntion(param1:String="hello",param2:int=3) {
And you can pass a reference to an object by saying:
//method of Class1
function myFuntion(obj:Object) { } //do something with obj
//in Class2
var class1:Class1 = new Class1();
class1.myFunction(this);
So the question, is there a keyword that can be used like:
//method of Class1
function myFuntion(obj:Object = CALLER) { } //do something with obj
//in Class2
var class1:Class1 = new Class1();
class1.myFunction();

The only default function parameter value that is accepted for the type Object is 'null'.
function myFunction(obj:Object = null):void {};
var class1:Class1 = new Class1();
class1.myFunction();

No, there isn't a way to what you ask, and that is a good thing for encapsulation and code readability. You should be forced to deliberately pass this so that it is clear to anyone reading Class2.as what your function is being given references to.
In general, you should ask yourself "why?" anytime you have a function parameter of type Object (that's pretty general!). I'm not saying there is never a good reason for it--for error reporting purposes, for example--but all too often it's the sign of poor OOP design (e.g. using an Object because you're too lazy to make a proper data structure class for what you're passing, or to circumvent typechecking)

Related

When using the 'Class' datatype, how can I specify the type so I only accept subclass of a specific class?

I've got a method that accepts a parameter of type Class, and I want to only accept classes that extend SuperClass. Right now, all I can figure out to do is this, which does a run-time check on an instance:
public function careless(SomeClass:Class):void {
var instance:SomeClass = new SomeClass();
if (instance as SuperClass) {
// great, i guess
} else {
// damn, wish i'd have known this at compile time
}
}
Is there any way to do something like this, so I can be assured that a Class instance extends some super class?
public function careful(SomeClass:[Class extends SuperClass]):void {
var instance:SuperClass = new SomeClass();
// all is good
}
If you are going to instantiate it anyway, why not accept an object instead which allows you to type it to :SuperClass?
careless(SomeClass);
//vs.
careless(new SomeClass);
Not too much of a problem there as far as your code goes.
There are a few differences though:
The object has to be created, because an object is required. If your function does not instantiate the class under some circumstances, this can be a problem. Additional logic to pass either an object or null can bloat the function call.
If you cannot call the constructor outside that function, it won't
work either.
All that is solved by the factory pattern. Pass a factory as the parameter that produces SuperClass objects.
function careful(factory:SuperClassFactory)
Your requirements:
I want to only accept classes that extend SuperClass
and
I need to pass in a Class so that it can be instantiated many times
by other objects later
Can be met by passing in an instance of the class you need, and using the Object.constructor() method.
public function careful(someInstance:SuperClass):void {
//you probably want to store classRef in a member variable
var classRef: Class = someInstance.constructor();
//the following is guaranteed to cast correctly,
//since someInstance will always be a descendant of SuperClass
var myInst:SuperClass = new classRef() as SuperClass;
}
More reading here.
You can't do that in ActionScript 3. In languages like C# you can do something like (forgive me if the syntax is off):
public void Careless<T>() where T : SuperClass
But AS3 does not have 'generics'. Unfortunately the only way I know how to do what you want is the way you have already done.
A pattern that might be more suitable for your use case might be something like:
class SuperClass
{
public static function careless():void
{
var instance:SuperClass = new SuperClass();
// ...
}
}
The only way to have static type checking in ActionScript 3 is to provide an instance of a class.
It is possible but it's expensive. You can use on a Class (not instance) the:
flash.utils.describeType
You then get an XML with a bunch of information including inheritance for that class. Like I said it's an expensive process and probably creating an instance and checking it will be in most cases faster.

Initializing unknown type in Actionscript 3.0

To keep my program running smoothly, I have to create objects only when I need them. In my case, I want to see if an object of an unspecified class is null, and if not, then call tell it to initialize. (I think "initialize" is the correct term, correct me if I am wrong)
if (someObject == null) {
someObject = new someObject.class()
}
Obviously, this code does not work. The idea seems right, but Actionscript does not agree with me. How can this be achieved?
Edit - Perhaps this will help to simplify (or complicate) the answer. I know that someObject extends my class SuperClass. Can I use a property from SuperClass to instantiate someObject?
If you know what type someObject is supposed to be, it's easy:
var someObject:SomeClass;
if(someObject==null)
{
someObject = new SomeClass(); // creates an instance of the class
}
If you don't, you're making it hard for yourself. You'll have to rethink your design I guess.
I'm going to make an attempt at getting on the right track toward a useful answer.
Firstly, looking at your example:
if (someObject == null) {
someObject = new someObject.class()
}
I'm not sure what you're trying to convey here. If someObject is null, you cannot extract any information about anything from it. Even if you type someObject e.g:
var someObject:Sprite;
You cannot access information about the fact that the property someObject is expecting a Sprite*
*You can use describeType() to access the type if someObject is a member of a class, but lets not go there.
If you want to be able to:
Check if an instance of a specific class exists, and if not;
Create an instance of the specified class.
You can write something like:
var existing:Object = { };
function findOrMake(type:Class):*
{
var key:String = type.toString();
if(!existing.hasOwnProperty(key))
{
// We need to make a new instance of this class.
existing[key] = new type();
}
return existing[key];
}
Which can be used like:
var someObject:Sprite = findOrMake(Sprite); // Makes a new Sprite.
someObject = findOrMake(Sprite); // References the above Sprite.
Don't name the instance with the exact name of the class you're instantiating.
If you do this:
var someobject;
if (someobject == null)
{
someobject = new someObject()
}
it works.

How to test whether a class instance is a dynamic type in AS3?

In AS3, if a class is marked as dynamic, new properties can be added and removed at runtime, by simply setting the property or removing it with the delete keyword.
I am asking whether there is a faster way to determine whether a class is dynamic than calling the describeType function and checking the "isDynamic" attribute value on the returned top-level XML node, for example: <type name="flash.display::MovieClip" base="Class" isDynamic="true" isFinal="true" isStatic="true">.
I suspect there is a faster method, but all I really need to do is try to assign a property value if it exists or can be created.
//The "base is dynamic" test is pseudo-code since it's not valid
if (base.hasOwnProperty(propertyName) || (base is dynamic))
base[propertyName] = value;
else
throw new Error( "Property " + propertyName + " does not exist and cannot be created." );
Perhaps I would be better off just wrapping the assignment in a try/catch block and assuming the class is not dynamic when the assignment fails. If it succeeds, I don't care whether it's dynamic, since the goal is to simply assign the property value if exists or can be added.
try{base[propertyName] = value}catch(err:Error){/*property did not exist and class is not dynamic, or some other error occurred in the property setter*/}
My only issue with the try/catch approach is that I wouldn't know if the assignment failed because the property couldn't be assigned or if some other error occurred in the property setter. Even catching the error and checking its type will not tell me whether the error occurred at this precise point (as opposed to some other setter deep within this setters calling chain), because the getStackTrace method is only available in the debug player. That's why I really need to check whether the class is dynamic up front, so the assignment failure can be reliably predicted and avoided altogether. I will opt for a correct implementation over a faster one.
My suggestion is to go the try/catch route. However, you actually can check to see if it failed because the property couldn't be assigned by either checking the errorID on the generic Error, OR you could catch that specific error before catching others. What you're looking for is 1056 which is a ReferenceError.
Here's an example of the 2nd method I mentioned:
var instanciatedSprite:Sprite = new Sprite();
var nonInstanciatedSprite:Sprite;
var dynamicMovieClip:MovieClip = new MovieClip();
for each(var obj:Object in [dynamicMovieClip, instanciatedSprite, nonInstanciatedSprite]){
try{
obj["abc"] = "abc";
}
catch(e:ReferenceError){
trace("property did not exist and class is not dynamic");
}
catch(e:Error){
trace("not the error you're looking for");
}
}
This will first trace out property did not exist and class is not dynamic when it attempts to assign a property to the instanciatedSprite. Then, when it hits the nonInstanciatedSprite, it will skip that catch and get caught by the generic catch for all other Error types and trace out not the error you're looking for.
Since the only correct way to determine whether the property can be assigned is to check whether the property exists and whether the property can be created, I decided to focus on optimizing the determination of whether the instance is dynamic.
Although the describeType function may be relatively slow, I really only need to call it once per type if I cache the results. I could then store the boolean result in a dictionary by type name or class reference, and then just use the much faster functions getQualifiedClassName and/or getDefinitionByName methods to look up whether the class was dynamic.
public class ClassUtils
{
static var typeDescriptions:Dictionary;
static var isTypeDynamic:Dictionary;
public function isDynamic( instanceOrClass:* ):Boolean
{
var qname:String = getQualifiedClassName(instanceOrClass);
var isDynamic:* = isTypeDynamic[qname];
if (isDynamic === undefined) //only explicitly untyped variables can hold the value undefined with strict equality
{
var desc:XML = getCachedTypeDescription( qname );
isDynamic = Boolean(desc.#isDynamic);
isTypeDynamic[qname] = isDynamic;
}
return isDynamic;
}
public function getCachedTypeDescription( qname:String ):XML
{
var desc:* = typeDescriptions[qname];
if (desc === undefined) //only explicitly untyped variables can hold the value undefined with strict equality
{
desc = describeType( type );
typeDescriptions[qname] = desc;
}
return desc;
}
}
That in turn will allow my original implementation to function quickly and efficiently:
if (base.hasOwnProperty(propertyName) || (ClassUtils.isDynamic(base))
base[propertyName] = value;
else
throw new Error( "Property " + propertyName + " does not exist and cannot be created." );

AS3 variable declared as a null function

I have encountered an AS3 function that is declared as a null variable, as in:
public var edgeWeights:Function = null;
I am not sure how to use this function to change null to another value (e.g., a number like 2 or 3). I thought something like cs.edgeWeights = 2 might work, but that creates a compile error, as does cs.edgeWeights(2);
I believe these are anonymous functions in AS3 and I did do some research on them, but could not find a resolution to this situation.
public var edgeWeights:Function = null;
This notation means declaring variable edgeWeights of type Function. In Actionscript Function is an object and can be set to null.
To use it you need to set this variable to some function. For example:
edgeWeights = function(a:int,b:int):int { return a+b } or edgeWeights = Math.sin.
What function you should set there depends on your particular case.
If you assume that the Class that declares edgeWeights is Widget:
protected var widget:Widget;
protected function createWidget():void {
widget = new Widget();
widget.edgeWeights = widgetCallback;
}
//signature would need to match what the Widget
//actually expects this callback to do
protected function widgetCallback():void {
trace('hi from widget callback');
}
Note that it's probably bad practice to have a public callback variable and not provide a default implementation, so if you have access to the source code, you should probably fix that.
Given any function:
public function someFunction()
{
...
}
You can create a "pointer" with this: this.edgeWeights = someFunction; (yes, without ())
Later you just use: this.edgeWeights(); and you'll be calling someFunction().

Function.call(someInstance,args) for methodes. Ignores first arg

In as3 there is a flexible way to change object instance, when calling it.
call or apply members of Function object can be called with specific first arg, and reference say us, that this first arg will be "this" pointer inside function. But i've found it wrong.
I'v write little test, listed below.
public class Test
{
private var name:String = "default";
public var test3:Function = test;
public var test2:Function = function()
{
trace(this.name);
}
public function Test(name:String)
{
this.name = name;
}
public function test():void
{
trace(this.name);
}
}
and tested it.
var tmp:Test = new Test("default");
tmp.test(); //out default
tmp.test.call(new Test("new")); //out default
tmp.test2(); //out default
tmp.test2.call(new Test("new2")); //out new2
tmp.test3(); //out default
tmp.test3.call(new Test("new3")); //out default
So, in anonymous function call we can get right output, but not in case of member function.
maybe it's becouse of ambiguous "this" pointer, that should reffer real object instance for correct work, maybe smth else. I dont now, and as3 reference didnt't describe smth about it.
Finally list of questions:
Why so? By me, it's very strange, and looks like undefined behaviour;
How i can achieve that functionality? How to deceive test function like anonymous one? Isn't it call methode target?
It isn't very important, but I'll be glad any good answer. Thanks!
P.S. sorry for my English.
//EDITED: added this statement to all "name" references. Nothing changes.
When invoking the [[Call]] property, the behavior is different for
different types of closures. A closure is an object that contains a
reference to a method, and the [[Call]] property acts differently
depending on whether it is a function, method, or class closure. A
function closure is one that is of a global method that isn't
associated with any instance of a class. A method closure contains an
instance method of a class, and will always remember its original
"this" value.
If the closure is a function closure, then the first argument passed
to [[Call]] is passed on to the method and gets used as the "this"
value. If the first argument is null or undefined, then the global
object will be used as the "this" value for the method.
If the closure is a method closure, then the first argument of
[[Call]] will be ignored, and the saved "this" value for the method
closure will be passed to the method as the first argument. A method
closure records what its original "this" value was and always uses
that instead of the first argument to [[Call]].
If the closure is a class closure, and there is 1 argument passed to
[[Call]] (in addition to the "this" argument), then the call is
treated as a type conversion, and the argument will be coerced to the
type represented by the closure.
http://learn.adobe.com/wiki/display/AVM2/2.4+Method+invocation+notes