Passing on superclass property values to subclass

Below I have a superclass called "myclass". Its constructor is written to initialize property 'a' with the 'input' argument, unless this argument is already an object of myclass, in which case it simply returns the object unchanged.
classdef myclass
properties
a=1;
end
methods
function obj=myclass(input)
if isa(input,'myclass')
obj=input; return
else
obj.a=input;
end
end
end
end
Both the following give the desired behavior,
>> objSuper=myclass(2)
objSuper =
myclass with properties:
a: 2
>> objSuper=myclass(objSuper)
objSuper =
myclass with properties:
a: 2
Now, I have a subclass called "mysubclass". Apart from intializing the superclass part of the object, the only desired behavior of its constructor is to modify the superclass property a, multiplying it by 10.
classdef mysubclass < myclass
methods
function obj=mysubclass(input)
obj=obj@myclass(input);
obj.a=10*obj.a;
end
end
end
As with the superclass, I would like the subclass constructor to be able to accept as input either a desired initializing value for 'a', or an object of type myclass itself, and use it accordingly to initialize the super-class part of the object.
The former works as I intend,
>> objSub=mysubclass(2)
objSub =
mysubclass with properties:
a: 20
The latter, however, gives me
>> objSub=mysubclass(objSuper)
When constructing an instance of class 'mysubclass', the constructor must preserve the class
of the returned object.
Error in mysubclass (line 9)
obj=obj@myclass(input);
I don't understand precisely why this error occurs. It appears that the command obj=obj@myclass(input) is somehow deleting the subclass part of 'obj', but as long as obj@myclass returns a valid object of type myclass (which it does), I don't see why the subclass constructor cannot handle that.

 Accepted Answer

I think what Adam said here is the key:
but it knows it is creating this as part of creating the subclass
In other words, the 'obj' output of the myclass() constructor is not an ordinary output variable. Not only is it pre-initialized when the myclass workspace is entered, but the pre-initialization type also depends on how myclass() was invoked. If it was invoked using a subclass object, as in subclassObj@myclass() then obj is pre-intialized to the subclass type. This is confirmed by the following,
classdef myclass
methods
function obj=myclass
disp ' '
disp(['obj is a ' class(obj) ' object'])
disp ' '
end
end
end
classdef mysubclass < myclass
methods
function obj=mysubclass
obj=obj@myclass;
end
end
end
which gives the behavior:
>> myclass;
obj is a myclass object
>> mysubclass;
obj is a mysubclass object
And therefore, assigning obj a superclass object, even in the superclass constructor, can force it to be the wrong type.

1 Comment

One remedy is to use the copyObject function proposed in this post and modify the myclass() constructor to,
function obj=myclass(input)
if isa(input,'myclass')
obj=copyObject(input,obj);
else
obj.a=input;
end
end

Sign in to comment.

More Answers (2)

Adam
Adam on 25 Jan 2016
Edited: Adam on 25 Jan 2016
A class constructor has some special constraints that other functions do not have. One of these is that it must returned an object of the class whose constructor it is.
You are trying to pass a ready-made object of a subclass into the superclass constructor and, if that is the case rather than you passing in a raw value, you want it to return you your subclass, but this is not possible because the superclass can only return an object of its own class from its constructor, not an object of a class derived from it.
Intuitively even in the first case this looks like something I would not advise doing, but I guess if it works (in the easier first case) it may not be that bad. Since Matlab functions do not allow overloads this would be the only way to allow both a normal constructor and a copy constructor, but I have never tried it (or wanted to!).
Personally I would be tempted to use some other approach than passing in a pre-created object to a class constructor - e.g. a static method on the class that creates the object and then sets properties as required. This should also get around the problem that you do get in the second case. i.e. if you already have an object then avoid even making a call to the constructor - have some wrapper code make the decision whether to call the constructor (to just pass in your value for 'a') or to simply create a copy of the object passed in if that is your goal.

4 Comments

Hi Adam,
You are trying to pass a ready-made object of a subclass into the superclass constructor
No, it is a ready-made superclass object that I am passing to both the myclass and mysubclass constructors. Note that the problematic call was
>> objSub=mysubclass(objSuper)
and that objSuper is still a superclass object in this example. Likewise, in the line
obj=obj@myclass(input);
the argument 'input' is a superclass object.
Adam
Adam on 25 Jan 2016
Edited: Adam on 25 Jan 2016
Yes, sorry, I misread that.
Ok, so it does appear to be that even when you are in the base class constructor your object should still be of the derived class type, you are just giving the base class portion of it to the base class constructor.
Usually in a constructor you do not explicitly assign something to obj - here you explicitly assign an object of the base class to obj but it knows it is creating this as part of creating the subclass so you cannot change the type of obj from the subclass type to the superclass type.
The exact nature of what goes on in constructor calls is not something I know much about, to be honest, but this is what I understand of it at least. In my class hierarchies I have never explicitly created an object in the base class, I just let the class create the object itself and I assign to it so I have never really looked deeply into it the exact class of obj before it leave the base class constructor.
Edit: I just checked one of my own class hierarchies and can confirm that even in the base class constructor the type of 'obj' after the first usage of it is that of the subclass even though it is the superclass part that is being constructed - the whole object is still of subclass type and this cannot change during any part of its construction.
Adam
Adam on 25 Jan 2016
Edited: Adam on 25 Jan 2016
Actually my original answer was so wrong that had you done what I thought you had done instead your code would have worked for the same reason I corrected myself about in the above post.
I have added a new answer that should be more sensible and basically include what I have added in a comment here. I can then delete this answer if you are happy that my other answer covers what I have said in the comments here.

Sign in to comment.

When creating an object of a subclass this is the object type that must be created in the construction process.
Whether you are constructing the base class part of the object or the derived class part the object type must still be that of the subclass if it is a subclass object you are creating.
Therefore your code hits an error because in the base class (myclass) you try to explicitly create an object of the base class type and assign it to obj. This is not allowed because obj must be of subclass type.
So actually if you were passing your objSub into the constructor instead of objSuper this should work fine because the class type is the expected one so the base class will assign the existing object of type objSub to its 'obj' and return it to the subclass constructor for it to complete construction.

Categories

Find more on Construct and Work with Object Arrays in Help Center and File Exchange

Tags

Asked:

on 25 Jan 2016

Commented:

on 26 Jan 2016

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!