ActionScript 3: Handling Inheritance

I’m working to migrate some code to ActionScript 3, and I’ve come across an annoying problem with inheritance. I’d like some ideas for how to work around it. It’s not a big deal, but I’d prefer not to add a lot of weird casting everywhere.

Here’s a basic example. FruitTree is my base class. It contains a variable called myFruit of type Fruit. I extend FruitTree to make AppleTree. In AppleTree, myFruit becomes an Apple, which is a subclass of Fruit. In AS3, I can’t seem to redeclare myFruit as an Apple inside AppleTree. Just rewriting the declaration in AppleTree (with Apple instead of Fruit) gives me an error, “A conflict exists with inherited definition FruitTree.myFruit in namepace public”. It seems to me that since Apple is a Fruit, things should stay compatible with the super class. I’d prefer to use this method because allows me to use myFruit as an Apple without casting it every time I need to use it.

Here’s an example of what worked fine using AS2:

class FruitTree
{
	public var myFruit:Fruit;

	public function doSomething():Void
	{
		this.myFruit.getEaten();
	}
}

class AppleTree extends FruitTree
{
	//An Apple is still a Fruit, but I want to access some special functions
	//In AS2, this works:
	public var myFruit:Apple;

	public function doSomething():Void
	{
		this.myFruit.removeStem(); //extra functionality
		this.myFruit.getEaten();
	}
}

In AS3, here’s the only way I’ve found to make it work, but requires a lot of extra code because I have to cast myFruit as Apple all the time.

public class FruitTree
{
	public var myFruit:Fruit;

	public function doSomething():void
	{
		this.myFruit.getEaten();
	}
}

public class AppleTree extends FruitTree
{
	//We can't redeclare the variable myFruit as an Apple because we get an error.

	public function doSomething():void
	{
		//Since we can't redeclare it, we have to cast it here
		//I'd rather not have to do this sort of casting a million times
		var myFruitAsApple:Apple = this.myFruit as Apple;
		myFruitAsApple.removeStem();
		myFruitAsApple.getEaten();
	}
}

I also tried building a getter and setter for the property as a work around, but changing the type in the overriding function gives an “Incompatible override” error. It’s probably for the same reason as the code above. Can anyone give me any insights into a way to do what I want? Could this be classified as a bug?

About Josh Tynjala

Josh Tynjala is a frontend developer, open source contributor, bowler hat enthusiast, and karaoke addict. You might be familiar with his project, Feathers UI, an open source user interface library for Starling Framework that is included in the Adobe Gaming SDK.

Discussion

  1. JesterXL

    I don’t understand why you are trying to redeclare it. It’s already declared in your base class, and thus AppleTree can access it without having to declare it since it’s inherited.

    Can you elaborate on why you wish to re-declare it again?

  2. Josh Tynjala

    In FruitTree, myFruit is a Fruit. Within AppleTree, myFruit is an Apple. I never intend for it to be any other sort of Fruit (except subclasses of Apple, of course). Any time I want to access methods or properties that are specific to Apple, I have to cast it as an Apple first. I might have to do this dozens or hundreds of times. In AS2, I could just use one redeclaration and it was an Apple everywhere. I liked that.

  3. David R

    What about doing something like this? (this is probably wrong syntax, as I don’t do much AS3 yet)

    class AppleTree {
    var myFruit:Fruit;

    public function get myApple():Apple {
    return myFruit as Apple;
    }

    function stuff() {
    myApple.removeStem(); //etc
    }
    }

  4. Josh Tynjala

    That would work, David. Good thinking. I don’t think I’d like having to make a seperate myMacintosh property if I were to write a MacintoshAppleTree class, though.

    The best practice is probably casting it as an Apple every time. However, I wonder if there are any reasons why redeclaring a variable as a different, but compatible, type would be bad. It’s a little more work for the compiler, I’d guess, but I can’t think of any arguments against it off the top of my head.

  5. todd anderson

    hello josh.

    i wouldn’t classify it as a bug. i think the problem stems from declaration and inheritance.

    here is something i whipped up to show how to easily switch between eating fruit between trees ( never thought i;d ever type that sentence 🙂 ) – http://custardbelly.com/AS3/FruitApp/srcview/index.html

    Basically it has interfaces for a FruitTree and a Fruit, with “abstract” classes for each to be used for subclassing.

    During the doSomething method you would call the methods ‘prepare’ and ‘eat’ – from the prepare inheritance, Apple has a call to a removeStem method, and likewise if you wanted to make other fruits, an Orange could have a call to a peel method.

    -todd

  6. Josh Tynjala

    That’s an excellent example, Todd. It shows why a developer would want to use interfaces (something many have trouble understanding right away), and it helps illustrate how one can abstract an action (such as removing a stem) into a more generalized form (like preparing the fruit) so that it can be used across many different kinds of related objects. You should post it on your blog. I’m sure a lot of people could benefit from it.

    I’m currently migrating a good deal of code (probably a hundred classes or more) from AS2 to AS3. Building the original code, I ran into many situations just like that, and I used similar solutions. Some objects have more obvious connections than others, obviously. I’ll have to spend some time looking for nice generalizations like that. Now’s probably the best time to do it, too, since we’re working on such a big transition.

  7. Mike J

    I would actually take Davids example and make it a bit more generic. Haven’t done AS3 yet, but to illustrate:

    Class FruitTree {
    private _myFruit:Fruit;
    public function get myFruit():Fruit { … }
    }

    Class AppleTree {
    public function get myFruit():Apple {
    return _myFruit as Apple;
    }
    }

    That way, you can still access it using this.myFruit but it should be cast correctly without adding boatloads of extra code.

  8. george

    Probably not the best idea, but simple. You can just use a wildcard in myFruit declaration. myFruit:*

  9. Josh Tynjala

    Here’s an interesting solution that is similar to David’s.

    //In FruitTree class
    protected function initializeFruit():void
    {
    this.myFruit = new Fruit();
    }

    //In AppleTree class
    override protected function initializeFruit():void
    {
    //AppleTree now includes a myApple variable
    this.myApple = new Apple();
    this.myFruit = this.myApple;
    }

    It still clutters the class with an additional property, which I’m not a fan of. I’d use this one over David’s to bypass the overhead of calling a function and casting myFruit as an Apple.

  10. ryan

    I agree with the interface assessment. I may be missing something regarding the intended usage, but the interface structure is there exactly for the case where you want to pass multiple types around in one place. In conjunction with a superclass, you can then have a class that you can pass around as any of the interfaces that it implements, while still allowing it to inherit common functionalities (or override) from the superclass. Taking advantage of this (or being forced to do this 😉 ) also helps force you into thinking a in a bit more organized fashion, which is nice sometimes…

  11. tom

    ok, here is what I consider a serious bug in the inheritance of as3

    class A {
    private const MYCONST:String = “A”;
    public function A(){}

    public function toString(){ return this.MYCONST; }
    }

    class B extends A {
    private const MYCONST:String = “B”;
    public function B(){ super() }
    }

    trace( new A() ); // prints “A”;
    trace( new B() ); // prints “A” ?!?!?!?!??!

  12. Josh Tynjala

    tom, I’m pretty certain that is the correct behavior. As a private constant, MYCONST will always be “A” within class A and that’s where the toString() method is located. MYCONST as specified in class B is a completely different constant that cannot be accessed from class A.

  13. Eric Hawthorne

    Regarding the original problem.
    Look up the terms “covariant inheritance”
    and “contravariant inheritance”

    O-O programming languages take different points of view on this
    issue. Both POVs have merits.

    The contravariant case says: Look: I might want to have a setter method on the FruitTree that says e.g. setFruit(f:Fruit).
    Now since the proper interpretation of inheritance is “isa” then an AppleTree isa FruitTree, and whatever I can call on a FruitTree, I must be able to also call on an AppleTree. So I call
    aFruit:Pear = Pear()
    myAppleTree.setFruit(aFruit)
    and, according to the contravariant interpretation of “isa”
    contracts, that argument needs to be accepted, because it meets
    the type criteria on the setFruit method.

    Covariant inheritance (of method argument type constraints)
    would say no, what we are doing is defining a specialization
    hierarchy of whole-part objects and we need structural (part)
    specialization when we specialize the type of the “whole”.
    So we should allow the setFruit(f:Fruit) method to throw
    an “incompatible part type” exception if we send a Pear in
    to be part of an AppleTree.

    Java takes the contravariant view for method argument type
    constraints, but allows covariantly specialized return types
    on method return values, which is less controversial, since
    a covariant (more specialized type returned by method call
    on subclass) still meets the typ constraint specified on the
    general class version of the method declaration.

  14. Youdgin

    I have a problem if someone can help. I have a double linked list implemented and for a class ListNode wich has properties next:ListNode and prev:ListNode (wich are instances of their own class) and obj:*. My problem is how do I inherit the class ListNode let’s say with the class ListGeniNode(that is what actualy i need) in that way so i can access methods of the child class through the properties of the parent class. Example:
    public class ListNode{
    public var next:ListNode;
    public var prev:ListNode;
    public var obj:*
    //constructor goes here
    }
    public class ListGeniNode extends ListNode{
    //constructor goes here
    public function get geniTreeNode():GeniTreeNode{
    return super.data as GeniTreeNode;
    }
    }
    class main{
    function main():void{
    var node:ListGeniNode = new ListGeniNode();
    node.next.obj = new GeniTreeNode();
    trace(node.(next as ListGeniNode).geniTreeNode);//returns null but not the object GeniTreeNode
    }
    }

    And i sort of know that you cant cast the parent instance to child but is there a way around this and any suggestions are welcomed.
    The reason is the proper reusing of the Class ListNode(because the getter is available only for GeniTreeNode type objects) and to know what to do if I find similar cases like this. Thank You!