Home > Software design >  How to access derived class members in abstract implementation of method?
How to access derived class members in abstract implementation of method?

Time:05-07

How is it that Derived is perfectly happy to call this.log() but somehow doesn't know about this.value? I'd have thought value would be set before super() is called?

How can Derived have a value set by the constructor and still be accessed by this.log()?

abstract class Base
{
    protected abstract map() : void;    

    constructor()
    {
        this.map();
    }
}

class Derived extends Base 
{
    constructor(private value : string)
    {
        super();        
    }

    protected map(): void 
    {
        this.log();
    }

    log()
    {
        alert(this.value);
    }
}

const derived = new Derived("Hello world"); // Alerts "undefined"

Playground

CodePudding user response:

This is because when you call super() the value property is not yet initialised and it is mandatory to call parent class's constructor which means you must call super() at the top of the constructor.

So as you are calling map method inside the Base class the value property is not yet initialised and thats why you are getting undefined.

Execute the following code and see the flow:

abstract class Base
{
    protected abstract map() : void;    

    constructor()
    {
        console.log("abstract base constructor")
        this.map();
    }
}

class Derived extends Base 
{
    constructor(private value : string)
    {
        super();
        console.log("value initialized",this.value)  
    }

    protected map(): void 
    {
        console.log("map function call")
        this.log();
    }

    log()
    {
        console.log("log functon call")
        alert(this.value);
    }
}

const derived = new Derived("Hello world"); // Alerts "undefined"

It's a good idea to call map method inside the Derived class constructor after calling super()

Playground

CodePudding user response:

@Fabrizio Gargiulo and @Shubham Wajo already mentioned the root problem which is from super() initialization before this.value assignment, but I'd give another way to fix it with a call stack trick.

Javascript only has a single thread, so all functions will proceed synchronously from top to bottom. super() is not an exception for it, but we can delay the execution with setTimeout for the call stack completed (all initializations are done).

abstract class Base
{
    protected abstract map() : void;    

    constructor()
    {
        //this will run after `Derived` initalization completed
        setTimeout(() => this.map())
    }
}

class Derived extends Base 
{
    constructor(private value : string)
    {
        super();        
    }

    protected map(): void 
    {
        this.log();
    }

    log()
    {
        alert(this.value);
    }
}

const derived = new Derived("Hello world"); //"Hello world"

Playground

CodePudding user response:

As far as I know, inside the Derived constructor, the first thing you can call is super(), only after you can initialize your Derived parameter "value". The problem is that you call this.map() before that value is ever initialized and for this reason the Alert shows undefined.

So you have two options in my opinion:

  1. You declare a protected property called "value" (or "_value") in Base class, and you init it before the this.map() statement.
  2. You declare and init such property in the Derived class, but, this.map() should be executed in Derived constructor.

Solution 1

abstract class Base
{
    protected abstract map() : void;    

    constructor(protected value : string)
    {
        this.map();
    }
}

class Derived extends Base 
{
    constructor(protected value : string)
    {
        super(value);    
    }

    protected map(): void 
    {
        this.log();
    }

    log()
    {
        alert(this.value);
    }
}

const derived = new Derived("Hello world"); // Alerts "undefined"

Solution 2

abstract class Base
{
    protected abstract map() : void;    

}

class Derived extends Base 
{
    constructor(private value : string)
    {
        super();              
        this.map();
    }

    protected map(): void 
    {
        this.log();
    }

    log()
    {
        alert(this.value);
    }
}

const derived = new Derived("Hello world"); // Hello world
  • Related