Fill object properties from another class object

Hi there,
I load an object of a class 'xdTest' from an API and would like to add to this object one more property.
I first thought of creating a class 'Test' with my extra property inheriting the subclass 'xdTest' properties like :
classdef Test < xdTest
properties
myExtraProp
end
end
But the thing is that I can't manage to create a Test object and fill its inherited properties with an existing xdTest object.
Any idea of a way to fill or merge its inherited properties without having to do it for every property in a constructor method?
Thank you so much!

2 Commenti

Adam
Adam il 29 Giu 2016
Modificato: Adam il 29 Giu 2016
A lot depends on what you intend to do with this object later on. As I'm sure you are aware there are two main ways to provide extended behaviour in this way. You can use inheritance as you are suggesting and the answers below pick up on or you can use aggregation/composition.
Which to use depends on your situation although I have heard the phrase (or similar) 'use aggregatation/composition when you can and inheritance when you have to'.
So in your case you could simply have your class Test include an object of the class xdTest rather than inherit from it. i.e. it would be one of its properties.
This comes with pros and cons, hence me saying it depends what further interaction you want as to which is better. Using inheritance you inherit all the methods and properties with their access specifiers set by the base class. Using aggregation/composition you can make the xdTest part of the class either private or public. If you make it private you can then provide only such access via your new class as is needed.
The whole point of all that was that if you use aggregation/composition you don't need to copy the properties of your xdTest object into a Test object because you just set the whole xdTest object as a property of the Test object.
Clément E.
Clément E. il 30 Giu 2016
Modificato: Clément E. il 30 Giu 2016
Thank you very much for having reviewed the different possibilities Adam. Composition is currently what I am using, but inheritance seemed to me more appropriate. You may have changed my mind :)

Accedi per commentare.

 Risposta accettata

Assuming that the base class does not have a copy constructor (a constructor that simply takes another object of the base class) I think the only way is indeed to copy all the properties. And that's assuming that the properties have public or protected set access.
You can automate that though:
classdef Test < xdTest
properties
myExtraProp
end
methods
function this = Test(xdTestobj, extraprop)
mco = ?xdTest;
for prop = mco.PropertyList'
if ismember(prop.SetAccess, {'public', 'protected'})
this.(prop.Name) = xdTestobj(prop.Name);
else
warning('could not copy property %s', prop.Name);
end
end
this.myExtraProp = extraprop;
end
end
end

7 Commenti

Thank you very much Guillaume. I tried to do something like what you propose (your code is pretty neat though) but the thing is that the xdTestobj returned by the API is an Object Array (21x1 xdTest), so :
this.(prop.Name) = xdTestobj(prop.Name);
would eventually return an error.
Oh, the fact that arrays of object is the same as the object is one thing I don't get on at all with matlab's object model. I guess that makes sense in matlab's view that everything is an array, but that really complicates the design of classes as you've got to constantly be on your toes.
In this case, you just (!) have to wrap the property copying into a loop
The difficulty comes in creating the array in the first place, since, as far as I know, there's no way of creating arrays of objects with arbitrary ranks in the constructor.
You could always create 2D arrays, but if the object being copied happens to have more than 2 dimensions, it'll be reshaped into 2d:
function this = Test(xdTestobj, extraprop)
[rows, cols] = size(xdTestobj); %if more than 2 dimensions, cols will be cols*size(remaining dims)
this(rows, cols) = Test; %creates a 2D array
mco = ?xdTest;
proplist = mco.PropertyList'; %transpose for easy iteration
proplist = proplist(ismember({prop.SetAccess}, {'public', 'protected'}); %remove private properties since we can't set them
%note that you should also remove properties that are dependent, transient, abstract, etc.
for objidx = 1:numel(xdTestobj)
for prop = proplist
this(objidx).(prop.Name) = xdTestobj(objidx).(prop.Name);
end
this.myExtraProp = extraprop;
end
end
Actually, I've just thought that you can do the property copying without a loop using cell array to comma-separated-list expansion:
function this = Test(xdTestobj, extraprop)
[rows, cols] = size(xdTestobj); %if more than 2 dimensions, cols will be cols*size(remaining dims)
this(rows, cols) = Test; %creates a 2D array
mco = ?xdTest;
proplist = mco.PropertyList'; %transpose for easy iteration
proplist = proplist(ismember({prop.SetAccess}, {'public', 'protected'}); %remove private properties since we can't set them
%note that you should also remove properties that are dependent, transient, abstract, etc.
for prop = proplist
propvals = {xdTestobj.(prop.Name)};
[this.(prop.Name)] = propvals{:};
end
[this.MyExtraProp] = extraprop; %scalar expansion
end
The issue is still creating the array in the first place.
Ah! I just though of a way to create an array of arbitrary size and rank. Simply create a vector of the appropriate size and reshape:
function this = Text(xdTestobj, extraprop)
this(numel(xdTestobj)) = test; %create vector
this = reshape(this, size(xdTestobj));
%now copy properties
...
end
Wow, that's impressive. Thank you!
The line
this(numel(xdTestArray)) = Test
is calling the constructor method so I had to add the following line to deal with it :
if nargin == 0, return; end
Then, when copying properties from xdTestArray object, I have the following error :
You cannot set the read-only property 'testRef' of Test.
That may be due to the (SetAccess=private) attribute of some properties in the xdTest class.
Do you know a way to not inherit or override those attributes from the xdTest class ?
Thanks!!
Oh, yes you need to prevent the infinite recursion in the constructor!
The code I've written should skip over private setaccess properties. That's the whole point of the ismember filter. I must have missed something. As per my comment, you probably also need to check a few other properties such as readonly, transient, etc.
Unfortunately, you won't be able to copy the private properties.
Your code was working great, but I needed to copy private properties. I think I'll go with the Composition way which seems, after all, more appropriate for my case.
Thank you again for your precious help and time!

Accedi per commentare.

Più risposte (1)

Steven Lord
Steven Lord il 28 Giu 2016
You want to call the superclass constructor to initialize the superclass properties, then have the subclass constructor set the properties defined only in the subclass? See this section from the documentation for instructions on how to call the superclass constructor from the subclass constructor.

6 Commenti

Wouldn't the subclass need to have a constructor that accepts an object of the subclass (i.e. a copy constructor) for this to work?
No. See for example the four classes whose code is given at the end of this documentation page. Each of DocStock, DocBond, and DocSavings have constructors that accept different information specific to stocks, bonds, or savings accounts respectively. They use those asset-type-specific information to derive/compute the current value for that asset and pass that current value into the DocAsset base class constructor. Then they store the asset-type-specific information in the properties defined in each type's subclass.
I don't see this as being the same at all. Using your Doc* example, what is being asked is given a DocAsset object and values for NumShare and SharePrice, create a DocStock object with these. So the DocStock constructor signature would be:
function this = DocStock(DocAssetobj, numShare, SharePrice)
this = ???? %create a copy of DocAssetobj
%I don't think there is a way other than copying each property individually
this.numShare = numShare;
this.SharePrice = SharePrice;
end
Jan Orwat
Jan Orwat il 29 Giu 2016
Modificato: Jan Orwat il 29 Giu 2016
@Steven, @Guillame, you stucked to your guns, I believe both of you are right. What @Clément asked for was to convert from one class to the other and add some properties while keeping original class as superclass. I can see three options:
  1. Use @Guillame constructor solution and remember that each time we want to construct object of our class, we have to create original object first.
  2. Make constructor as @Steven suggested, and separately create conversion method like function classBObj = classA2classB(ClassAObj, extras)
  3. Combine those two behaviours in class constructor by checking class of first input and following dedicated scenario then.
While scenarios 2 and 3 may look more versatile, 1st one may be better in some scenarios. If the only construction case for creating classBObj is when classAObj already exist then 2nd scenario makes little sense, if it is not same rule applies to 1st scenario.
Guillaume
Guillaume il 29 Giu 2016
Modificato: Guillaume il 29 Giu 2016
I don't see it as sticking to our gun, as I see as misunderstanding either on my part or on Steve's.
With your scenario 2 or 3, the question is still how do you get the properties of classAObj into the classBObj object. I don't think there's a mechanism other than copying the properties (which Clément wanted to avoid).
Moreover, the signature of classBObj = classA2classB(ClassAObj, extras) is that of a constructor (there's no classBObj input), so I don't see this being any different from option 1.
But then again, maybe I'm misunderstand something.
Oh, sorry. It seems I misused the phrase. I meant you both are right. And yes, no matter which scenario we choose, the same problem to solve remains.
You are right, classA2classB should be a separate function or classA method then.

Accedi per commentare.

Categorie

Scopri di più su Class Introspection and Metadata in Centro assistenza e File Exchange

Community Treasure Hunt

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

Start Hunting!

Translated by