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