Discover MakerZone

MATLAB and Simulink resources for Arduino, LEGO, and Raspberry Pi

Learn more

Discover what MATLAB® can do for your career.

Opportunities for recent engineering grads.

Apply Today

Thread Subject:
Changing class of object

Subject: Changing class of object

From: ?yvind

Date: 11 Dec, 2008 10:05:03

Message: 1 of 9

I am trying to convert new-style user-defined objects from a superclass to a subclass, and having great difficulty with it.

I have one superclass, class0, which has several subclasses, class1, class2, class3, class4, which are slight variations on class0, with some restrictions on property values, and perhaps an extra property.

For certain objects of class0 I know they actually fulfill the requirements to belong to one of the subclasses, and I want to convert the class of those objects. Right now, it seems that the only way to convert obj0 of class0 into obj1 of class1, is to construct an empty obj1 and then meticulously copy each non-dependent propertyvalue from obj0 to obj1. And this goes for all the subclasses.

In the old system, you simply did
>> obj1 = class(struct(obj0),'class1');
(or perhaps you needed to add an extra field), and off you go. Now, I have tried giving obj0 as input to the class1 constructor--having made sure that all class constructors accept objects as input--e.g.
>> obj1_new = class1(obj1)
is OK. But I cannot find a way to do
>> obj1 = class1(obj0)
without getting the error

??? When constructing an instance of class 'class1', the
constructor must preserve the class of the returned object.

Any idea if this is even possible without going the long route of property-by-property copying, which I find tedious and error-prone?

?yvind

Subject: Changing class of object

From: Doug Schwarz

Date: 11 Dec, 2008 13:10:23

Message: 2 of 9

In article <ghqokf$p20$1@fred.mathworks.com>,
 "?yvind" <oyvist@gmail.com> wrote:

> I am trying to convert new-style user-defined objects from a superclass to a
> subclass, and having great difficulty with it.
>
> I have one superclass, class0, which has several subclasses, class1, class2,
> class3, class4, which are slight variations on class0, with some restrictions
> on property values, and perhaps an extra property.
>
> For certain objects of class0 I know they actually fulfill the requirements
> to belong to one of the subclasses, and I want to convert the class of those
> objects. Right now, it seems that the only way to convert obj0 of class0 into
> obj1 of class1, is to construct an empty obj1 and then meticulously copy each
> non-dependent propertyvalue from obj0 to obj1. And this goes for all the
> subclasses.
>
> In the old system, you simply did
> >> obj1 = class(struct(obj0),'class1');
> (or perhaps you needed to add an extra field), and off you go. Now, I have
> tried giving obj0 as input to the class1 constructor--having made sure that
> all class constructors accept objects as input--e.g.
> >> obj1_new = class1(obj1)
> is OK. But I cannot find a way to do
> >> obj1 = class1(obj0)
> without getting the error
>
> ??? When constructing an instance of class 'class1', the
> constructor must preserve the class of the returned object.
>
> Any idea if this is even possible without going the long route of
> property-by-property copying, which I find tedious and error-prone?
>
> ?yvind

Please post the code for your constructor(s).

--
Doug Schwarz
dmschwarz&ieee,org
Make obvious changes to get real email address.

Subject: Changing class of object

From: ?yvind

Date: 12 Dec, 2008 10:54:02

Message: 3 of 9

Doug Schwarz <see@sig.for.address.edu> wrote in message <see-E7FF6E.08102311122008@news.frontiernet.net>...
> In article <ghqokf$p20$1@fred.mathworks.com>,
 
> Please post the code for your constructor(s).
>
> --
> Doug Schwarz
> dmschwarz&ieee,org
> Make obvious changes to get real email address.

Superclass constructor:
function obj = scan(varargin)
            if ~isempty(varargin),
                if isa(varargin{1},'scan'),
                    obj = varargin{1};
                else
                    set(obj,varargin{:});
                    obj.Pos = scan.coord_format_check(obj.Pos);
                end
          end
end

Subclass constructor:
function obj = bpscan(varargin)
         obj = obj@scan(varargin{:});
         obj.Pos = bpscan.coord_format_check(obj.Pos);
end

Now I get the error:
"??? When constructing an instance of class 'bpscan', the constructor
must preserve the class of the returned object."

One way around this is, as I said, copying each and every property from the superclass object into a subclass object, but that isn't really straightforward, what with dependent properties and so on, and also I have several subclasses, which would all have to include lots of field-by-field copying code.

I seems that in Java, casting from superclass to subclass is trivial, so I thought there would be some straightforward way in Matlab as well.

?yvind

Subject: Changing class of object

From: Doug Schwarz

Date: 12 Dec, 2008 15:05:57

Message: 4 of 9

In article <ghtfsa$lq9$1@fred.mathworks.com>,
 "?yvind" <oyvist@gmail.com> wrote:

> Doug Schwarz <see@sig.for.address.edu> wrote in message
> <see-E7FF6E.08102311122008@news.frontiernet.net>...
> > In article <ghqokf$p20$1@fred.mathworks.com>,
>
> > Please post the code for your constructor(s).
> >
> > --
> > Doug Schwarz
> > dmschwarz&ieee,org
> > Make obvious changes to get real email address.
>
> Superclass constructor:
> function obj = scan(varargin)
> if ~isempty(varargin),
> if isa(varargin{1},'scan'),
> obj = varargin{1};
> else
> set(obj,varargin{:});
> obj.Pos = scan.coord_format_check(obj.Pos);
> end
> end
> end
>
> Subclass constructor:
> function obj = bpscan(varargin)
> obj = obj@scan(varargin{:});
> obj.Pos = bpscan.coord_format_check(obj.Pos);
> end
>
> Now I get the error:
> "??? When constructing an instance of class 'bpscan', the constructor
> must preserve the class of the returned object."
>
> One way around this is, as I said, copying each and every property from the
> superclass object into a subclass object, but that isn't really
> straightforward, what with dependent properties and so on, and also I have
> several subclasses, which would all have to include lots of field-by-field
> copying code.
>
> I seems that in Java, casting from superclass to subclass is trivial, so I
> thought there would be some straightforward way in Matlab as well.
>
> ?yvind



Okay, I see what you mean and I don't see any other way around it
either. Sorry.

You could make a method of scan be a copy operation that would loop
through the properties and copy them automatically. Something like

  function obj = copy(obj,superobj)
      prop = properties(superobj);
      for i = 1:length(prop)
          obj.(prop{i}) = superobj.(prop{i});
      end
  end

then in the bpscan constructor you can test if the input is of class
scan and, if so,

  obj = obj.copy(varargin{1});

This is untested, but I think it should work.

N.B.: properties(superobj) will only find unhidden properties. To find
all property names you can use

  prop = fieldnames(struct(superobj));

instead.

--
Doug Schwarz
dmschwarz&ieee,org
Make obvious changes to get real email address.

Subject: Changing class of object

From: Steven Lord

Date: 12 Dec, 2008 15:09:57

Message: 5 of 9


"?yvind" <oyvist@gmail.com> wrote in message
news:ghtfsa$lq9$1@fred.mathworks.com...
> Doug Schwarz <see@sig.for.address.edu> wrote in message
> <see-E7FF6E.08102311122008@news.frontiernet.net>...
>> In article <ghqokf$p20$1@fred.mathworks.com>,
>
>> Please post the code for your constructor(s).
>>
>> --
>> Doug Schwarz
>> dmschwarz&ieee,org
>> Make obvious changes to get real email address.
>
> Superclass constructor:
> function obj = scan(varargin)
> if ~isempty(varargin),
> if isa(varargin{1},'scan'),
> obj = varargin{1};
> else
> set(obj,varargin{:});
> obj.Pos = scan.coord_format_check(obj.Pos);
> end
> end
> end
>
> Subclass constructor:
> function obj = bpscan(varargin)
> obj = obj@scan(varargin{:});
> obj.Pos = bpscan.coord_format_check(obj.Pos);
> end

Both these constructors are in classdef files and the bpscan classdef file
starts with:

classdef bpscan < scan

to indicate that bpscan is a subclass of scan, correct?

> Now I get the error:
> "??? When constructing an instance of class 'bpscan', the constructor
> must preserve the class of the returned object."

This sort of sounds like bpscan doesn't inherit from scan, and so when you
call obj@scan MATLAB thinks you're trying to make the bpscan object into a
scan object.

--
Steve Lord
slord@mathworks.com

Subject: Changing class of object

From: ?yvind

Date: 12 Dec, 2008 15:58:02

Message: 6 of 9

"Steven Lord" <slord@mathworks.com> wrote in message <ghtus5$2a4$1@fred.mathworks.com>...
>
> Both these constructors are in classdef files and the bpscan classdef file
> starts with:
>
> classdef bpscan < scan
>
> to indicate that bpscan is a subclass of scan, correct?

Correct.

> > Now I get the error:
> > "??? When constructing an instance of class 'bpscan', the constructor
> > must preserve the class of the returned object."
>
> This sort of sounds like bpscan doesn't inherit from scan, and so when you
> call obj@scan MATLAB thinks you're trying to make the bpscan object into a
> scan object.

Well it does inherit from scan, or so I believe. Everything has been working fine, properties and methods inherited and so on. It is *only* when I pass a scan (superclass) object into the bpscan (subclass) constructor that things go wrong.

?yvind

Subject: Changing class of object

From: ?yvind

Date: 12 Dec, 2008 16:41:02

Message: 7 of 9

Doug Schwarz <see@sig.for.address.edu> wrote in message <see-5A7EB7.10055712122008@news.frontiernet.net>...
> Okay, I see what you mean and I don't see any other way around it
> either. Sorry.
>
> You could make a method of scan be a copy operation that would loop
> through the properties and copy them automatically. Something like
>
> function obj = copy(obj,superobj)
> prop = properties(superobj);
> for i = 1:length(prop)
> obj.(prop{i}) = superobj.(prop{i});
> end
> end
>
> then in the bpscan constructor you can test if the input is of class
> scan and, if so,
>
> obj = obj.copy(varargin{1});
>
> This is untested, but I think it should work.
>
> N.B.: properties(superobj) will only find unhidden properties. To find
> all property names you can use
>
> prop = fieldnames(struct(superobj));
>
> instead.
>
> --
> Doug Schwarz
> dmschwarz&ieee,org
> Make obvious changes to get real email address.

Hmm. Maybe. I'll see how it works out. But some properties are dependent, and shouldn't be set at all, and there's all sorts of stuff going on with set-methods and get-methods and whatnot. Anyway, there ought to be some built-in behaviour for this type of casting.

?yvind

Subject: Changing class of object

From: Doug

Date: 11 Mar, 2010 21:16:05

Message: 8 of 9

I came upon this post by searching on the "object class must be preserved" message. It's not a particularly clear message, and had me chasing my tail for a while.

After a lot of thought and reading of the docs (and previous posts), it started to make sense. You can't pass a subclass object to a parent class constructor from within the subclass constructor. For instance if you have a Person class and a Man subclass, you can't pass the "in construction" Man object to the Person constructor. The Man object is not fully instantiated until it exits its constructor. This especially makes sense for value classes.

I had a similar situation where each subclass had its own standard value for an attribute. I'm new to Matlab's object conventions, and went through some variations before I came up with a (to me) satisfactory variation. Here's a quick example of what I did, in the order of revisions I went through.

Option 1 I didn't like in my situation, because it caused me to have to explicitly set the gender property in the subclass constructor. With lots of subclasses and properties, it was easy to forget, and was open to having the property be inconsistent with the subclass. Also, it required the subclass author to know to set the gender - no compiler warnings.

Option 1
--------------

classdef Person
   properties
      gender;
   end
end
classdef Man
    % set, get methods as appropriate
   methods
      function obj = Man()
         obj.gender = 'male';
      end
   end
end

Option 2 was an improvement, in that I couldn't get the wrong value for the property for each subclass. Unfortunately, the fallout was that I had to reference gender like this: Man.gender. In other words, I couldn't loop through a list of Person objects, and get their gender. I would have to inspect each object to determine its subclass to appropriately reference its constant.

Option 2
-------------
classdef Person
   properties (Abstract, Constant)
      gender
   end
end
classdef Man
   properties(Constant)
      gender='male';
   end
end

Option 3 fixed the looping issue. It's now possible to loop through a list of Person objects, and be assured there is an accessible gender property. However, it seemed strange to have to redefine the property (gender) for every subclass. The property isn't really different for each subclass, only the value. Again, with lots of properties and subclasses, it was open to coding inconsistencies. And still lots of responsibility on the subclass author to provide the correct gender.

Option 3
------------
classdef Person
   properties (Abstract)
      gender
   end
end
classdef Man
   properties (Constant, SetAccess = private, GetAccess = private)
      private_gender = 'male';
   end
   properties (Dependent=true, GetAccess = public)
      gender = Man.private_gender;
   end
end

I ended up with Option 4. This moves the get method into the parent class, and the only thing required of the subclass author is to provide the protected_gender constant -- and the compiler warns if it is left out. Now only the value changes across subclasses, not the property or the property methods.

Option 4
---------------
classdef Person
   properties (Abstract, Constant, GetAccess=protected)
      protected_gender;
   end
   properties(Dependent=true, SetAccess = private)
      gender;
   end
   methods
      function value = get.gender(obj)
         value = obj.protected_gender;
      end
   end
end
classdef Man
   properties (Constant, GetAccess = protected)
      protected_gender = 'male';
   end
end

After working through this, I realized it was very close to the Template design pattern. The Parent class is implementing the logic behind providing the gender (the Template), while the subclasses provide the details, i.e., the actual value.

This worked well for me in my situation. Kind of a long rant, but hope it helps someone out there.

Subject: Changing class of object

From: Paul

Date: 6 Mar, 2012 18:37:18

Message: 9 of 9

The best solution is to
Replace Constructor with Factory Method as described in Martin Fowler's Refactoring: Improving the Design of Existing Code

classdef PersonType
  properties
    Type;
  end
  
  methods
    
    function obj = PersonType(person)
      if nargin ~= 0
        obj.Type = person;
      end
    end
    
  end
  
  methods(Static)
    function obj = create(person) %#ok<STOUT>
      % factory method for creating people
      try
        eval(['obj = ', person, ';']);
      catch me
        error([obj, 'is currently not supported']);
      end
      
    end
  end
 
end


classdef Man < PersonType
  properties
    Gender = 'Male';
  end
  
  methods
    function obj = Man()
      obj = obj@PersonType('Man');
    end
  end
  
end


classdef Woman < PersonType
  properties
    Gender = 'Female';
  end
  
  methods
    function obj = Woman()
      obj = obj@PersonType('Woman');
    end
  end
  
end
 





"Doug " <douglaskline@acm.org> wrote in message <hnbmil$990$1@fred.mathworks.com>...
> I came upon this post by searching on the "object class must be preserved" message. It's not a particularly clear message, and had me chasing my tail for a while.
>
> After a lot of thought and reading of the docs (and previous posts), it started to make sense. You can't pass a subclass object to a parent class constructor from within the subclass constructor. For instance if you have a Person class and a Man subclass, you can't pass the "in construction" Man object to the Person constructor. The Man object is not fully instantiated until it exits its constructor. This especially makes sense for value classes.
>
> I had a similar situation where each subclass had its own standard value for an attribute. I'm new to Matlab's object conventions, and went through some variations before I came up with a (to me) satisfactory variation. Here's a quick example of what I did, in the order of revisions I went through.
>
> Option 1 I didn't like in my situation, because it caused me to have to explicitly set the gender property in the subclass constructor. With lots of subclasses and properties, it was easy to forget, and was open to having the property be inconsistent with the subclass. Also, it required the subclass author to know to set the gender - no compiler warnings.
>
> Option 1
> --------------
>
> classdef Person
> properties
> gender;
> end
> end
> classdef Man
> % set, get methods as appropriate
> methods
> function obj = Man()
> obj.gender = 'male';
> end
> end
> end
>
> Option 2 was an improvement, in that I couldn't get the wrong value for the property for each subclass. Unfortunately, the fallout was that I had to reference gender like this: Man.gender. In other words, I couldn't loop through a list of Person objects, and get their gender. I would have to inspect each object to determine its subclass to appropriately reference its constant.
>
> Option 2
> -------------
> classdef Person
> properties (Abstract, Constant)
> gender
> end
> end
> classdef Man
> properties(Constant)
> gender='male';
> end
> end
>
> Option 3 fixed the looping issue. It's now possible to loop through a list of Person objects, and be assured there is an accessible gender property. However, it seemed strange to have to redefine the property (gender) for every subclass. The property isn't really different for each subclass, only the value. Again, with lots of properties and subclasses, it was open to coding inconsistencies. And still lots of responsibility on the subclass author to provide the correct gender.
>
> Option 3
> ------------
> classdef Person
> properties (Abstract)
> gender
> end
> end
> classdef Man
> properties (Constant, SetAccess = private, GetAccess = private)
> private_gender = 'male';
> end
> properties (Dependent=true, GetAccess = public)
> gender = Man.private_gender;
> end
> end
>
> I ended up with Option 4. This moves the get method into the parent class, and the only thing required of the subclass author is to provide the protected_gender constant -- and the compiler warns if it is left out. Now only the value changes across subclasses, not the property or the property methods.
>
> Option 4
> ---------------
> classdef Person
> properties (Abstract, Constant, GetAccess=protected)
> protected_gender;
> end
> properties(Dependent=true, SetAccess = private)
> gender;
> end
> methods
> function value = get.gender(obj)
> value = obj.protected_gender;
> end
> end
> end
> classdef Man
> properties (Constant, GetAccess = protected)
> protected_gender = 'male';
> end
> end
>
> After working through this, I realized it was very close to the Template design pattern. The Parent class is implementing the logic behind providing the gender (the Template), while the subclasses provide the details, i.e., the actual value.
>
> This worked well for me in my situation. Kind of a long rant, but hope it helps someone out there.

Tags for this Thread

What are tags?

A tag is like a keyword or category label associated with each thread. Tags make it easier for you to find threads of interest.

Anyone can tag a thread. Tags are public and visible to everyone.

Contact us