In ActionScript 3 is there a way (a hack - maybe through square brackets, maybe through cloning an Object, maybe through prototype, maybe through namespaces, ...) to change a private or protected member of a class?
For example if I have an IconToast class delivered by a someLibrary.swc and I know it has a
protected var windowOptions:WindowOptions;
Can I change it somehow? I've tried many things, for example:
var errorToast:IconToast = new IconToast();
errorToast.addButton("Dismiss");
errorToast.message = "Error when connecting";
errorToast['windowOptions'].timeout = 10 * 1000;
errorToast.show();
(gives me runtime error ReferenceError: Error #1069: Property windowOptions not found on IconToast and there is no default value).
Do this by extending the base class and giving public access to the private/protected method/property etc.
public class MyIconToast extends IconToast
{
public function getWindowOptions():WindowOptions
{
return windowOptions;//here you can access protected (not private though:);
}
}
for private it may not be possible but similar to above solution to some extent it can be done
Best regards
Nope, There is no way to change its modifier directly( That's why it's defined as public, protected and private, right?). The solution of Lukasz 'Severiaan' Grela is OK, but it can be better: use getter/setter to retrieve read/write function.
public function set windowOption(option:int):void;
public function get windowOption():int;
and the private attributes belong ONLY to their class, so if they're packed into SWC, you can't see any of them:D.
Related
My question is specific to as3.
When I use this language, it seems to me that any variable with a getter and setter should be made public instead.
Whether you do this :
public class Test
{
private var _foo:String;
public function Test()
{
foo = "";
}
public function get foo():String
{
return _foo;
}
public function set foo(value:String):void
{
_foo = value;
}
}
or this :
public class Test
{
public var foo:String;
public function Test()
{
foo = "";
}
}
you will end up doing this eventually (to get or set your foo variable from another class) :
testObject.foo
And using a public variable looks much cleaner to me.
I know that I am missing something.
Could you please show me what it is?
Before we continue, understand that when you define getters and setters, they don't actually need to be associated with a property defined within the class. Getters simply have to return a value, and setters have to accept a value (but can do absolutely nothing if you wish).
Now to answer the question:
The most simple reason is that you can make properties read or write only, by declaring one without the other. In regards to read only, take a moment to consider the benefits of having a class expose a value without other parts of your application being able to modify it. As an example:
public class Person
{
public var firstName:String = "Marty";
public var lastName:String = "Wallace";
public function get fullName():String
{
return firstName + " " + lastName;
}
}
Notice that the property fullName is the result of firstName and lastName. This gives a consistent, accurate value that you would expect if firstName or lastName were to be modified:
person.firstName = "Daniel";
trace(person.fullName); // Daniel Wallace
If fullName was actually a public variable alongside the other two, you would end up with unexpected results like:
person.fullName = "Daniel Wallace";
trace(person.firstName); // Marty - Wait, what?
With that out of the way, notice that getters and setters are functions. Realize that a function can contain more than one line of code. This means that your getters and setters can actually do a lot of things on top of simply getting and setting a value - like validation, updating other values, etc. For example:
public class Slideshow
{
private var _currentSlide:int = 0;
private var _slides:Vector.<Sprite> = new <Sprite>[];
public function set currentSlide(value:int):void
{
_currentSlide = value;
if(_currentSlide < 0) _currentSlide = _slides.length - 1;
if(_currentSlide >= _slides.length) _currentSlide = 0;
var slide:Sprite = _slides[_currentSlide];
// Do something with the new slide, like transition to it.
//
}
public function get currentSlide():int
{
return _currentSlide;
}
}
Now we can transition between slides in the slideshow with a simple:
slideshow.currentSlide = 4;
And even continuously loop the slideshow with consistent use of:
slideshow.currentSlide ++;
There are actually many good reasons to consider using accessors rather than directly exposing fields of a class - beyond just the argument of encapsulation and making future changes easier.
Here are some of the reasons:
Encapsulation of behavior associated with getting or setting the property
this allows additional functionality (like validation) to be added more easily later.
Hiding the internal representation of the property while exposing a property using an alternative representation.
Insulating your public interface from change allowing the public interface to remain constant while the implementation changes without affecting existing consumers.
Controlling the lifetime and memory management (disposal) semantics of the property particularly important in non-managed memory environments (like C++ or Objective-C).
Providing a debugging interception point for when a property changes at runtime - debugging when and where a property changed to a particular value can be quite difficult without this in some languages.
Improved interoperability with libraries that are designed to operate against property getter/settersMocking, Serialization, and WPF come to mind.
Allowing inheritors to change the semantics of how the property behaves and is exposed by overriding the getter/setter methods.
Allowing the getter/setter to be passed around as lambda expressions rather than values.
Getters and setters can allow different access levels for example the get may be public, but the set could be protected.
for my current project I am starting to work with AS3 and I have written a ClipManager class where I can define an MC like "mainView" during initialization like this:
clipManager:ClipManager = new ClipManager(mainView);
With my clipManager I can now easily load stuff into the mainView etc. The problem is that I want every button throughout the whole thing to access Class Methods of this instance to alter the mainView. Can I have something like a global Class instance in Flash or is there any smarter way to achieve what I am trying to do?
You can either add your ClipManager class as a static somewhere - i.e. a god object - (perhaps your main class) and access it through that, or you can use the Singleton pattern.
A common way to implement it in as3:
public class Singleton
{
private static m_instance:Singleton = null; // the only instance of this class
private static m_creating:Boolean = false;// are we creating the singleton?
/**
* Returns the only Singleton instance
*/
public static function get instance():Singleton
{
if( Singleton.m_instance == null )
{
Singleton.m_creating = true;
Singleton.m_instance = new Singleton;
Singleton.m_creating = false;
}
return Singleton.m_instance;
}
/**
* Creates a new Singleton. Don't call this directly - use the 'instance' property
*/
public function Singleton()
{
if( !Singleton.m_creating )
throw new Error( "The Singleton class can't be created directly - use the static 'instance' property instead" );
}
}
Now, to access your class, you call Singleton.instance. There'll only ever be one instance of this class.
As for anti-patterns etc, well that's another post :)
Possibly bad practice but I'm not well versed in software design anyway (I'm sure this question would have been asked before but I can't seem to find the right terminology)...Anyhow, it's just another curiosity of mine I'd like to have answered.
So I have worked in a way where I type a base class variable to type Object or Sprite or something similar so that in my subclasses, I can instantiate my custom classes into them and store it. And when I access it, I just cast that variable to ensure I can access the methods.
Take this example, so that you know what I'm talking about:
public class BaseClass
{
protected var the_holder_var:Object;
public function BaseClass()
{
//Whatever abstract implementation here...
}
}
Now, my subclasses of that base class usually use an interface but for simplicity sake, I'll just write it without it.
public class AnExtendedClass extends BaseClass
{
public function AnExtendedClass()
{
//Instantiate my own class into the base class variable
this.the_holder_var = new ACustomClassOfMine();
//Then I can use the 'hackish' getter function below to
//access the var's functions.
this.holder_var.somefunction()
}
private function get holder_var():ACustomClassOfMine
{
return this.the_holder_var as ACustomClassOfMine;
}
}
This works and I'm sure it will make some ppl cringe (I sometimes cringe at it too).
So now, my question, is there a way to recast/retype that base var in my extended subclass?
kinda like this:
public class ExtendedClass extends BaseClass
{
//Not possible I know, but as a reference to see what I'm asking about
//Just want to change the type....
override protected var the_holder_var:ACustomClassOfMine;
public function ExtendedClass()
{
//Then I can forget about having that hackish getter method.
this.the_holder_var = new ACustomClassOfMine();
this.the_holder_var.somefunction();
}
}
I was thinking of typing most of my base class vars that I use as holders as type * and retyping them as I extend the class. (I could use it here too but yeah...)
Thoughts? Comments? Ideas?
I actually think your code (apart from the hypothetical addition at the end) is pretty alright. The practise of adding accessors to solve the type issue you're dealing with is a solid one. I would advise to rename the accessor to show it is a cast, maybe get holderVarAsCustom():ACustomClassOfMine (I'm also not a big fan of the underscores, that's another language's convention), but that's personal preference. What I'd do to solve your last problem is just create a matching setter function:
private function set holderVarAsCustom(value:ACustomClassOfMine):void {
this.the_holder_var = value;
}
This way you can access the correctly typed holder var for both read and write operations with complete type safety:
holderVarAsCustom = new ACustomClassOfMine();
holderVarAsCustom.someFunction();
I would definately advise against dropping the type safety by including arrays and what not, that just makes it unstable.
I must admit that i'm a little confused as to why you want to do this, but here goes. Could you not utilise the fact that Array's can hold different data types. So something like this:
public class BaseClass
{
protected var customStorage:Array;
public function BaseClass()
{
//Whatever abstract implementation here...
}
}
You could then access it with an associative method and a property:
public class AnExtendedClass extends BaseClass
{
private static const myName:String = "myName";
public function AnExtendedClass()
{
//Instantiate my own class into the base class variable
customStorage[myName] = new ACustomClassOfMine();
objectIWant.somefunction()
}
private function get objectIWant():ACustomClassOfMine
{
return ACustomClassOfMine(customStorage[myName]);
}
}
Is that any better?
I would not try to tinker this behaviour, since you can't change the declared type of a variable once declared, no matter how hard you try.
What I do in such cases, I either cast the variable if I use it sparingly or the object it references may change, or I add another variable with the type I want and let the other variable point to the new one. Like this:
public class A {
protected var object:Object;
public function A() {
//Whatever abstract implementation here...
}
}
and
public class B extends A {
protected var other:MyClass;
public function B() {
super();
this.other = new MyClass();
this.object = this.other;
}
}
Having it this way, class A uses the object via the this.object reference, and class B can use the this.other or both. But both references point to the same object. The only issues with this are:
having two references for in the same class to the same object is ugly (so are untyped variables and casts)
if the object one of them may point can change during runtime, you must be really carefull to synchronize these changes
Hi I'm wondering if I can have a packageless () AS3 class call a private method on the main class in the file. For example:
package demo
{
public class MyDemoClass
{
var helper:FriendlyHelperClass = new FriendlyHelperClass(this)
}
private function methodToCall():void
{
...
}
}
public class FriendlyHelperClass
{
public function FriendlyHelperClass(demo:MyDemoClass)
{
demo.methodToCall()
}
}
The call to methodToCall() from FriendlyHelperClass will fail as it is a private member of the MyDemoClass. Is there any way to call the methodToCall() method from the FriendlyHelperClass without extending MyDemoClass.
Basically I'm looking for inner class functionality that Java has or some sort of C++ style friend class.
Short answer : no.
You can never access a private member from outside a class in ActionScript. What you could do is use a namespace instead of a private scope. This would allow to give access to some members to selected classes. This is the closest of a friend class that you will get in AS3.
I'm afraid that is not possible, but if you make the class dynamic, then you can edit it while the program is running, and possibly add some useful functions to it, to access the private functions. I haven't tried it though.
Dynamic classes
Without testing the code, and knowing what your full problem. you can try passing the functions you need into the embedded class as a callback. e.g.,
helper.methodToCallCallback = this.methodToCall;
then inside FriendlyHelperClass:
this.methodToCallCallback();
I have carried the method here on almost all of the areas where I have had overridable methods and managed to fix them but there is one part where the method doesnt work in the same way on a different contexted piece of code:
public Employee()
{
this.InitMembers();
}
private void InitMembers()
{
// Init the collection so it's never null
this.Territories = new List<Territory>();
}
public Employee(string firstName, string lastName): this()
{
this.reffirstName = firstName;
this.reflastName = lastName;
}
> public virtual IList<Territory> Territories { get; protected set; }
Where again the > is the code causing the error, I do however get an intellisense option to "Convert to auto property", which simply reverts the code to when it was started and not fixing the problem.
Anyone know what modifications need to be made to this part to elimiate the fxcop violation?
The error appears because your private constructor is calling a method that can be overridden from a derived class. To fix the warning, you need to remove any calls to virtual methods from within the constructor.
In the example you list, InitMembers uses 'this.Territories', which is causing the violation. According to your later comment you have added a private member - use that instead.