I am calling a superclass method from a subclass. When called from a subclass, ‘this’ in the superclass method no longer refers to the superclass but to the subclass that called it. This creates problems with inheritance.
Details as follows:
In my project I use a class to construct objects from registered types. The types are registered from properties, or alternatively from a template based on an already defined type:
class A{ createObject(type) { let object = null; for (let i in this.objecttypes) { if (this.objecttypes[i].type == type) { if (this.objecttypes[i].template) { object = this.createObject(this.objecttypes[i].template); object.type = type; } else object = new TestObject(type); for (const aname in this.objecttypes[i].attributes) object.set(aname, this.objecttypes[i].attributes[aname]); //console.log("Object: "+JSON.stringify(object)); } } return object; } }
This works fine in the superclass. However, I have also constructed a subclass with more user-friendly methods. In the subclass, the method called ‘createObject’ doesn’t return the created object. It stores it and returns the object id:
class B extends A{ createObject(type,otherargs){ let object=super.createObject(type); this.objects.set(object.id,object); /* do things with otherargs */ return object.id; } } /* Method to test function. In test class */ templateObjects(){ let container=this.getContainer(); console.log("proto: "+JSON.stringify(Object.getPrototypeOf(container))); let tt=App.createObjectType("template1","",[{name:"attr0",value:1},{name:"attr1",value:2},{name:"attr2",value:3}]); let ut=App.createObjectType("test","template1",[{name:"attr3",value:66}]); container.addObjectType(tt); container.addObjectType(ut); let o0=container.createObject("template1"); console.log("Object: "+JSON.stringify(o0)); let o1=container.createObject("test"); console.log("Object: "+JSON.stringify(o1)); }
When I now try to create template-based objects from the subclass, and the superclass code comes to this point:
if (this.objecttypes[i].template) { object = this.createObject(this.objecttypes[i].template); object.type = type; }
the ‘this.createObject(…)’ call results in calling the subclass’ createObject-method, thus returning a numerical key, and failing on trying to assign the type to it.
I know I could for instance rename the method in the subclass to avoid the problem, I could send in the class object (the ‘this’) as a parameter or other tricky stuff.
But is there a more straightforward, idiomatic way to solve this?
Advertisement
Answer
this
refers to an object, not to a class. In this case, it’s an instance of B
in normal use (even in code in A
— there’s a single object). Since the createObject
property of the object comes from B
, that’s what you get from this.createObject
(regardless of where that code occurs).
But there’s a more fundamental issue:
When you subclass something, you’re saying that it is a more specialized version of the thing you subclassed. That is, B
is a specialised A
. That means B
is an A
. A
has a particular contract, e.g. it returns an object from its createObject
method. By changing that return type in B
, you’re breaking that contract. B
is no longer an A
, it’s something A
-like except for createObject
.
So I recommend not doing that, since it breaks one of the rules of subclassing.
You can call A
‘s version of createObject
from the code in A
, like this:
object = A.prototype.createObject.call(this, this.objecttypes[i].template);
but I strongly recommend that you don’t. 🙂 Instead, don’t make B
break A
‘s contract. Use a different method for what you’re doing in B
.