AS3 Casting as SuperClass returns null - actionscript-3

THE SOLUTION!
Ensure that you're not mistakenly importing the wrong class.
In my case, I was explicitly importing TabButton, but there was
another TabButton class that was being imported because it existed in
the same directory as TabButtonSubclass. This happened because
TabButtonSubclass exists in a folder specific to one application. This
folder also contains a horribly generically named class, TabButton,
which is completely unrelated and incompatible to the other TabButton
class. The application-specific TabButton class was renamed so it's
obvious it belongs to that application.
Had I written 'public class MBTabButtonSprited extends [specific
location].TabButton', it would have worked fine. But it seems that
since 'extends TabButton' was not prefaced by a specific path, the
compiler defaulted to the local path.
I've been banging my head on my desk for a while now, trying to figure this one out.
My issue is that I've extended Sprite to create a TabButton class. I then extended the TabButton class (we'll call it TabButtonSubclass) for some additional functionality. When I try to cast an instance of TabButtonSubclass as TabButton, I get null.
The bizarre part though, is that I can cast an instance of TabButton or TabButtonSubclass as a Sprite, and it works fine.
Here is what my TabButton looks like:
import flash.display.Sprite;
public class TabButton extends Sprite {
public function TabButton():void {
super();
// code
}
}
And here is what my TabButtonSubclass looks like:
import ...TabButton;
public class TabButtonSubclass extends TabButton {
public function TabButtonSubclass( bitmap:BitmapData ):void {
// code
super();
}
}
To clarify the TabButtonSubclass class: The purpose of extending the TabButton was so that you could instantiate a TabButtonSubclass with a supplied Bitmap. This Bitmap would be used for various purposes. I have extended classes in the past and added parameters to the constructor, and it worked fine. Is this not a good idea?
Some sample code that illustrates my issue:
import flash.display.Sprite;
import ...TabButton;
import ...TabButtonSubclass;
var btn1:TabButton = new TabButton();
trace( "btn1:", btn1 ); // btn1: [object TabButton]
trace( "btn1 as Sprite:", (btn1 as Sprite) ); // btn1 as Sprite: [object TabButton]
var btn2:TabButtonSubclass = new TabButtonSubclass( new GangTabMail() );
trace( "btn2:", (btn2) ); // btn2: [object TabButtonSubclass]
trace( "btn2 as TabButton:", (btn2 as TabButton) ); // btn2 as TabButton: null
trace( "btn2 as Sprite:", (btn2 as Sprite) ); // btn2 as Sprite: [object TabButtonSubclass]
As you can see, I'm able to cast btn1 as Sprite with no issues. I can also cast btn2 as a Sprite, and it returns the expected results. However, if I try casting an instance of TabButtonSubclass as TabButton, it returns null.
I'm interested in casting an instance of TabButtonSubclass as TabButton because the application utilizes a couple of classes with that require objects of type TabButton to be passed.
Here's an example of some code that allows you to type a class as a super class:
import ...TabButton;
import flash.display.Sprite;
var myBtn:TabButton = new TabButton();
doStuff( myBtn ); // btn: [object TabButton]
function doStuff( btn:Sprite ):void {
trace( "btn:", btn );
}
Here's an example of the code failing if you go one level deeper:
import ...TabButton;
import ...TabButtonSubclass;
import flash.display.Sprite;
var myBtn:TabButtonSubclass = new TabButtonSubclass( new BitmapData( ... ) );
doStuff( myBtn ); // Returns error: Implicit coercion of a value of type ...:TabButtonSubclass to an unrelated type ...:TabButton
doStuff( myBtn as TabButton ); // btn: null
function doStuff( btn:TabButton ):void {
trace( "btn:", btn );
}
Can anybody offer any help as to why I get a null response when I attempt to trace it out? Or why I get that error? Am I missing something in regards to subclasses and type casting?

I'm not sure why it would return null, but the way I would handle it would be to rather define the variable using the base class and cast up when needed, eg.
import ...TabButton;
import ...TabButtonSubclass;
import flash.display.Sprite;
var myBtn:TabButton = new TabButtonSubclass( new BitmapData( ... ) );
doStuff( myBtn ); // No need to case here since myBtn is already defined as TabButton
function doStuff( btn:TabButton ):void {
trace( "btn:", btn );
}
Then if you need TabButtonSubclass functionality you cast it up:
var myBtnSub:TabButtonSubclass = myBtn as TabButtonSubclass;

Your code should work perfectly. However this is not what other call the "best practice" when you declare a type of a function parameter as a Class. You should have interface for your TabButton.
You can pass tons of different objects to your function with one exception: objects must have all methods implemented, that your interface has. That's it. See Polymorphism
One possible answer for your question is:
public class TabButtonSubclass extends TabButton {
the TabButton above and the TabButton below are different classes
trace( "btn2 as TabButton:", (btn2 as TabButton) ); // btn2 as TabButton: null
A key difference between casting and the as operator is the behavior on failure. When a cast fails, a TypeError is thrown. With the as operator whenever a cast fails the default value for the datatype is returned. read more...

I'm not an expert in AS3 and still learning, but perhaps this can help:
In your last code snippet, when you do
doStuff( myBtn );
and
doStuff( myBtn as TabButton);
what you are doing there is you are passing a variable with type TabButtonSubclass when the function is expecting a variable with a type of TabButton. So that's why you get the first error about implicit coercion.
Now the reason why you get a null if you try to cast your variable as TabButton when passing it into the function, I'm not really sure but I think it has to do with that if a cast can't be "honored" (i.e, you tell the compiler that the cast is valid, but it turns out that it actually is not, like if you tried to cast XML data as a movie clip, for example), then the result will be a null.
Hope that helps, but I could be totally wrong!

Ensure that you're not mistakenly importing the wrong class.
In my case, I was explicitly importing TabButton, but there was another TabButton class that was being imported because it existed in the same directory as TabButtonSubclass. This happened because TabButtonSubclass exists in a folder specific to one application. This folder also contains a horribly generically named class, TabButton, which is completely unrelated and incompatible to the other TabButton class. The application-specific TabButton class was renamed so it's obvious it belongs to that application.
Had I written 'public class MBTabButtonSprited extends [specific location]. TabButton', it would have worked fine. But it seems that since 'extends TabButton' was not prefaced by a specific path, the compiler defaulted to the local path.

Related

Call to possibly undefined method dispatchEvent through a reference with static type GetColor

I'm writing a program using Main.as, that needs to listen to a function (getColor) in another class file (GetColor.as). I have the following in GetColor.as:
public class GetColor
{
public function getColor(event:MouseEvent):void
{
//doing stuff here
this.dispatchEvent(new Event("changeColor") );
}
}
and then in Main.as I have:
var getPicColor:GetColor = new GetColor();
getPicColor.addEventListener("changeColor",changeColorNow);
function changeColorNow(e:Event):void
{
//do stuff here
}
However, I am getting an error:
1061: Call to a possibly undefined method dispatchEvent through a reference
with static type GetColor.
What does this mean? I have nothing declared as static. Am I supposed to create an instance of dispatchEvent(), as opposed to using "this"?
You cannot dispatch events with a class that (implicitly) extends Object -> that's why you are getting there error -> where is "dispatchEvent()" method coming from? Where is it inherited from? (answer: it is not!)
Your GetColor class (horrible name there! :) ) must either extend a display object - which in your case it not really the correct solution, extend EventDispatcher or implement IEventDispatcher.
Then you can use the method dispatchEvent.

Error when overriding constructor of extended class

I found a quite strange problem while making two classes in AS3. Let's call them ParentClass and ChildClass. In order to make both of them you need a Sprite object, then the ParentClass makes it visible in the stage. ChildClass inherits the ParentClass, too.
ParentClass.as:
package myStudio.basic {
import flash.display.MovieClip;
import flash.display.Sprite;
public dynamic class ParentClass extends MovieClip {
public function ParentClass(mc:Sprite=null) {
addChild(mc);
}
}
}
ChildClass.as:
package myStudio.containers {
import myStudio.basic.ParentClass;
import flash.display.MovieClip;
import flash.display.Sprite;
public class ChildClass extends ParentClass {
public function ChildClass(mc:Sprite=null) {
addChild(mc);
}
}
}
Then, I write this code on Frame 1, Layer Actions of the FLA file:
var mc:MovieClip = new childMC;
var vig:ChildClass = new ChildClass(mc);
addChild(vig);
However, I got run-time error #2007:
TypeError: Error #2007: The value of the parameter child must not be null.
at flash.display::DisplayObjectContainer/addChild()
at myStudio.basic::ParentClass()
at myStudio.containers::ChildClass()
at myStudioComicAnimator_fla::MainTimeline/frame1()
I tried overriding the ChildClass constructor function, but it still doesn't work.
So here's my question: Is there another workaround to solve this problem?
The reason for that is that you are not calling super. You can check what's happening in the error stack (down to top):
you instantiate ChildClass, and you pass the previously created childMC to the constructor
ChildClass extends ParentClass, so when instantiated it always calls the constructor
the constructor of ParentClass tries to add something as a child
The problem is that you cannot add null as a child. But because the constructor is called internally, there is no param that is being passed to it. so mc variable is always null. But as we said - null cannot be added.
Use the super by yourself:
public function ChildClass(mc:Sprite=null) {
super(mc);
}
This way the ParentClass will get reference to the mc object and will be able to add it.
Another option is not to use addChild in the ParentClass, but only in ChildClass. Then it doesn't matter if you pass anything to super, or even if you are calling super at all.
Edit: I forgot to say that this is not a bug, but a standard behavior and works exactly like it should work. The reason for this is that each class can have a whole different override of the constructor. It can take more or less parameters, so the chain for calling parent's constructor is your job to handle.

How do one cast a instance of subclass to its superclass?

So I have this superclass grid class, and a subclass of the grid class named GrassTile1, GrassTile2, etc... all of the instance of the subclasses are stored in an array. How am I suppose to convert the instance of subclass to its superclass referencing to the array?
private var backgroundGrid = []; //the array which the grids are stored in, in the main class.
public class Grid extends MovieClip
{
protected var node :PathfindNode; //the variable I wish to access, from an instance of subclass.
public function Grid(){
node = new PathfindNode();
}
}
public class GrassTile1 extends Grid { //every subclass of Grid will extends Grid
public function GrassTile1() {
// constructor code
}
}
function getBackgroundGrid(i:int,j:int):Grid{ //in the main class
return Grid(backgroundGrid[i][j]); // this line gives me an error
}
TypeError: Error #1034: Type Coercion failed: cannot convert GrassTile1#2905d5f1 to Grid.
I've tried accessing backgroundGrid[i][j].node and other ways to work around that I could think of and failed. Any Idea?
Try :
return backgroundGrid[i][j] as Grid;
Personally, Grid seems like a bad class name to use. I think Tile makes more sense, as that GrassTile1 is not a grid as I logically understand a grid. A grid might contain a collection of tiles, so doesn't sound logical to use that as a class name for tiles.
Also, where is the line where you actually call the getBackgroundGrid method ? You should try casting there, as opposed to in that method. I believe that will solve the problem.
I can't verify the line throwing the error, so we are assuming that it's the return statement. But, it could be on the other side where you are calling getBackgroundGrid.
UPDATE : I have tried a .fla using what you are describing and it works just fine, I get no error. Which is why I'm thinking we are missing something here and maybe the definition of the class is not being used. Can you put a trace in your constructors to verify what you expect is actually happening ?

Call custom method as Display Object AS3

I have a class which extends MovieClip. This class has an update() function which needs to be called every new frame with the deltaTime in the arguments. This works if the class has been declared but not if it has just been added to the display list.
Code in the main class:
package packageFoo{
import flash.display.MovieClip;
import packageFoo.customMovieclip;
public class Main extends MovieClip{
public function Main():void{
var testMc:customMovieClip = new customMovieClip();
addChild(testMc);
testMc.update(dt);
}
}
}
This outputs the correct values where as if I just added it without referencing it:
package packageFoo{
import flash.display.MovieClip;
import packageFoo.customMovieclip;
public class Main extends MovieClip{
public function Main():void{
addChild(new customMovieclip());
this.getChildAt(0).update(dt);
}
}
}
This makes the compile time error: 1061: Call to a possibly undefined method update through a reference with static type flash.display:DisplayObject.
I can't really reference the 'customMovieclip's because I am wanting multiple ones.
It looks like this.getChildAt(0) is not customMovieClip. This can arise if your Main has pre-places components at design time. To check, do trace(this.numChildren) as the first line of Main() constructor. And also, to address any subclass methods properly, you need to typecast your DisplayObject returned by getChildAt() to a proper type.
(this.getChildAt(0) as customMovieClip).update(dt);
Still, using a class-wide variable is better if you want to address that custom MC in more than one function of main class.
If you're trying to avoid a reference to the custom class in the document class, you can call it like this:
this.getChildAt(0)["update"](dt);

Flash AS3 Dyanmic Text keeps giving an error 1119

So I have a method that takes in a String and then is suppose to set the dynamic textbox on a button to said String.
public function setText(caption:String) {
this.btext.text = caption;
}
I really don't understand why this method is producing a 1119 error.
Access of a possibly undefined property btext through a reference with static type Button.as
The instance name of the Dynamic Textbox is btext and I have tried deleting the textbox and making a new one however this still produces a 1119 error. I also read on another stack question that trying this['btext'].text = caption; which gave me plenty of runtime errors.
Basically what am I doing wrong?
Thank you for any help.
EDIT
Here is the code I am using, and I create an instance of button add it to the stage and store it in an array with this code.
Code to create button
this.buttonArray.push(this.addChild(weaponButton));
Button.as
package {
import flash.display.MovieClip;
import flash.filters.*;
public class Button extends MovieClip {
public function Button() {
}
public function setPosition(xpos:int, ypos:int) {
this.x = xpos;
this.y = ypos;
}
public function setScale(xScale:Number, yScale:Number) {
this.scaleX = xScale;
this.scaleY = yScale;
}
public function addDropShadow():Array {
var dropShadow:DropShadowFilter = new DropShadowFilter(2,45,0, 1,4,4,1,1,true);
return [dropShadow];
}
public function removeDropShadow():Array {
return null;
}
public function setText(caption:String) {
this.btext.text = caption;
}
}
}
As you have stated btext is an instance name of an object. Here is where I assume btext is an object you created in your library.
In your class you are doing 2 things wrong. So lets examine your method.
public function setText(caption:String) {
this.btext.text = caption;
}
The first thing wrong is you are using "this". "this" is a reference to the current instance of the class you are in. And you are saying btext is a property on said instance. Which as I am assuming it is not because you defined btext as an object in your library. This will give you the property is undefined error you are gettting.
Now the second issue at hand is you are about to ask "OK how do I reference btext in my class then". What you need to know is that only objects added to the display list IE:stage can access objects via the stage.
You can do this 3 ways.
The first way is to pass a reference to the button into the class and store it as a property of the class.
The second way is to add your class to stage and in the class listen to the addedToStage event. At that time you can then access the object.
MovieClip(root)["btext"].text
The first 2 methods are not good practice since btext is not apart of the class and a general rule of thumb would be to encapsulate your class.
To make this work what you could do is have your class assign the value to a property in your class then fire an event and make the parent of this class listen to that event then just grab the value and assign.
Here is some suggested reading
I think the variable btext doesn't exist at all, or is it inherited from Movieclip?