Search code examples
actionscript-3

Accessing properties after using getdefinitionbyname


I'm trying to access properties from a class like this:

public class Oak extends Tree //base class for tree classes
{
    public static const CLASS_NAME:String = getQualifiedClassName(Oak);

    public var branches:Array;

    public function Oak() 
    {
        branches = 
        [
        "Leaf1",
        "Leaf2",
        ];
    }

The class trying to access the variable is shown here:

public class TreeTest extends BaseScreen //just extends Sprite, TreeTest is shown on the stage
{
    public var oak:Tree;

    private var inc:int;

    public function TreeTest(pModel:Model) 
    {
        model = pModel;

        model.createTree(Oak.CLASS_NAME);
        oak = model.getTree(Oak.CLASS_NAME);
        inc = 0;

        this.addMouseClickListener(0, treeHandler); //from BaseScreen
    }

    private function treeHandler(e:MouseEvent):void 
    {
        inc++;
        this.displayText.text = oak.branches[inc];
    }

and the model is shown below:

public class Model 
{
    public var treeArray:Array;

    public function Model() 
    {
        treeArray = new Array();
    }

    public function createTree(pClassName:String):void 
    {
        var name:String = pClassName;
        var ClassReference:Class = getDefinitionByName(name) as Class;
        var classInstance:Tree = new ClassReference;
        treeArray.push([name, classInstance]);
    }

    public function getTree(pClassName:String):Tree
    {
        var treeName:String = pCharacterClassName;
        var match:Boolean = false;
        var matchArrayRef:int = 0;

        for (var i:int = 0; i < treeArray.length; i++) 
        {
            if (match == true) 
            {
                break
            }

            if (treeArray[i][0] == treeName) 
            {
                match = true;
                matchArrayRef = i;
            }
            else 
            {
                match = false;
            }
        }

        if (match == false) 
        {
            return null; 
        }
        else 
        {
            return treeArray[matchArrayRef][1];
        }
    }

When I ran this, I got the error "Access of possibly undefined property branches through a reference with static type Tree".

After searching for a solution, I discovered that basically means that the branches array was not in the Tree class. To confirm this, if I went and took public var branches:Array away from the Oak class and put it in the Tree class, the code worked.

Why can I not access variables defined in the Oak class, and have to put everything in the Tree base class? Even if I change var classInstance:Tree to var classInstance:Oak, or even var classInstance:Object, I still get the same error if the array is in the Oak class, not the Tree class.

Like, if I create a new class Pine which would also extend Tree, do I have to put all of Pine's variables in the Tree class too?


Solution

  • You are referencing the oak variable as class Tree, so any property you reference from it should be of class Tree, not any subclasses, because exactly the superclass does not have them, thus compiler does not know how to address memory from branches property. Worse, this addressing might technically differ from subclass to subclass, an that's why directly addressing properties of a subclass via reference of type superclass is not possible.

    There is a workaround. You can use hasOwnProperty method defined in Object class to check whether a certain tree has branches, and then address via string-based addressing like tree["branches"].

    if (tree.hasOwnProperty("branches")) {
        var br=tree["branches"];
        trace(br); //or whatever
    }
    

    In fact, you can even go like this:

    if (tree is Oak) {
        var oak:Oak=tree as Oak;
        // now all oaks have branches, go get em
        trace(oak.branches);
    }
    

    Still, this breaks one of the object-oriented programming's core principles - you attempt to explicitly depend on subclasses having variables of certain names and are expecting them to contain values of same meaning across subclasses. Or, with the second approach, you are trying to make code know about every single subclass of Tree that will ever exist. Effectively you try to make subclasses behave as a superclass, without giving the superclass enough properties to provide that behavior in a common manner. This is not how OOP works. So, you should first devise a superclass by assigning it properties and methods of a generic Tree. All trees have branches, so branches should belong to Tree class. All trees can grow() for example, with whatever set of parameters supplied to growth, be it humidity, warmth, soil thickness, etc, but they are the same for all subclasses of Tree, be it oaks or sequoias, thus, grow() method should belong to Tree class as well. They, however, grow differently, and respond to parameters differently, but this is where override comes to help provide different behavior in subclasses.