Unfortunately, all the elements of an array in MATLAB must be of the same type. When you concatenate different classes, MATLAB will attempt to convert them all to the same class.
If you've defined one of your classes as being inferior or superior to the other (using the InferiorClasses attribute or the INFERIORTO/SUPERIORTO functions), then the methods of the more superior class are invoked. If you haven't specified a relationship between the classes, then the two objects have equal precedence and MATLAB calls the left-most object method. This is likely why arr = [b c];
creates an array of class B and arr = [c b];
creates an array of class C.
Option 1: Cell arrays
If you want to execute the foo
method defined for class B on object b
, and also execute the foo
method defined for class C on object c
, then you will likely have to use cell arrays and the function CELLFUN. If foo
doesn't return a value, you could do something like this:
arr = {b,c};
cellfun(@foo,arr); % Invoke foo on each element of the cell array
Option 2: Fun with jury-rigging polymorphic behavior
For fun, I came up with a potential solution which technically works, but has some limitations. To illustrate the idea, I've put together a few sample classes similar to what you listed in the question. Here's the abstract superclass classA
:
classdef classA < hgsetget
properties
stuff
end
properties (Access = protected)
originalClass
end
methods
function foo(this)
disp('I am type A!');
if ~strcmp(class(this),this.originalClass)
this = feval(this.originalClass,this);
end
this.fooWorker;
end
end
methods (Abstract, Access = protected)
fooWorker(this);
end
end
And here's an example of the subclass classB
(classC
is exactly the same with everywhere B
replaced by C
and vice versa):
classdef classB < classA
methods
function this = classB(obj)
switch class(obj)
case 'classB' % An object of classB was passed in
this = obj;
case 'classC' % Convert input from classC to classB
this.stuff = obj.stuff;
this.originalClass = obj.originalClass;
otherwise % Create a new object
this.stuff = obj;
this.originalClass = 'classB';
end
end
end
methods (Access = protected)
function fooWorker(this)
disp('...and type B!');
end
end
end
The constructors for classB
and classC
are designed such that the two classes can be converted to one another. The property originalClass
is initialized at creation and indicates what the original class of the object was. This property will remain unchanged if an object is converted from one class to another.
Within the foo
method, the current class of the object passed in is checked against its original class. If they differ, the object is first converted back to its original class before invoking the fooWorker
method. Here's a test:
>> b = classB('hello'); % Create an instance of classB
>> c = classC([1 2 3]); % Create an instance of classC
>> b.foo % Invoke foo on b
I am type A!
...and type B!
>> c.foo % Invoke foo on c
I am type A!
...and type C!
>> arr = [b c] % Concatenate b and c, converting both to classB
arr =
1x2 classB handle
Properties:
stuff
Methods, Events, Superclasses
>> arr(1).foo % Invoke foo on element 1 (formerly b)
I am type A!
...and type B!
>> arr(2).foo % Invoke foo on element 2 (formerly c)
I am type A!
...and type C!
One key limitation (aside from being a little ugly) is the case where classB
and classC
each have properties that the other does not. In such a case, converting to the other class and then converting back would likely cause those properties to be lost (i.e. reset to their default values). However, if one class were a subclass of the other, such that it had all the same properties and thensome, there is a solution. You could set the subclass to be superior to the superclass (see discussion above) such that concatenating objects of the two classes will always cause the superclass objects to be converted to the subclass. When converted back within "polymorphic" methods (like foo
above), no object data will be lost.
I don't know how workable a solution this is, but maybe it will at least give you some interesting ideas. ;)