Tommy King
Tommy King

Reputation: 23

AS3 Casting as SuperClass returns null

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?

Upvotes: 1

Views: 526

Answers (4)

ZuzEL
ZuzEL

Reputation: 13645

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...

Upvotes: 0

Tommy King
Tommy King

Reputation: 23

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.

Upvotes: 0

cmann
cmann

Reputation: 1980

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;

Upvotes: 1

Garry Wong
Garry Wong

Reputation: 823

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!

Upvotes: 0

Related Questions