К сожалению, все элементы массива в MATLAB должны быть одного типа. Когда вы объединяете разные классы, MATLAB попытается преобразовать их в один класс.
Если вы определили один из ваших классов как более низкий или превосходящий другой (используя атрибут InferiorClasses или INFERIORTO / SUPERIORTO функции), то методы более высокого класса вызываются. Если вы не указали связь между классами, тогда эти два объекта имеют одинаковый приоритет, и MATLAB вызывает самый левый объектный метод. Вероятно, именно поэтому arr = [b c];
создает массив класса B, а arr = [c b];
создает массив класса C.
Вариант 1: Массивы ячеек
Если вы хотите выполнить метод foo
, определенный для класса B для объекта b
, а также выполнить метод foo
, определенный для класса C для объекта c
, то вам, вероятно, придется использовать массивы ячеек. и функция CELLFUN . Если foo
не возвращает значение, вы можете сделать что-то вроде этого:
arr = {b,c};
cellfun(@foo,arr); % Invoke foo on each element of the cell array
Вариант 2: Веселье с фальсификацией поведения присяжных
Ради интереса, я предложил потенциальное решение, которое технически работает, но имеет некоторые ограничения. Чтобы проиллюстрировать идею, я собрал несколько примеров классов, аналогичных тем, которые вы перечислили в вопросе. Вот абстрактный суперкласс 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
А вот пример подкласса classB
(classC
точно такой же, где везде B
заменено на C
и наоборот):
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
Конструкторы для classB
и classC
разработаны таким образом, что эти два класса могут быть преобразованы друг в друга. Свойство originalClass
инициализируется при создании и указывает, каким был исходный класс объекта. Это свойство останется неизменным, если объект будет преобразован из одного класса в другой.
В методе foo
текущий класс передаваемого объекта проверяется по сравнению с его исходным классом. Если они различаются, объект сначала преобразуется обратно в исходный класс, прежде чем вызывать метод fooWorker
. Вот тест:
>> 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!
Одним из ключевых ограничений (помимо того, что он немного уродлив) является случай, когда classB
и classC
имеют свойства, которых нет у другого. В таком случае преобразование в другой класс и последующее обратное преобразование может привести к потере этих свойств (т. Е. Сбросить их значения по умолчанию). Однако, если один класс является подклассом другого, так что он имеет все те же свойства и, следовательно, некоторые, есть решение. Вы можете установить, что подкласс превосходит суперкласс (см. Обсуждение выше), так что объединение объектов двух классов всегда приведет к преобразованию объектов суперкласса в подкласс. При обратном преобразовании в «полиморфные» методы (например, foo
выше) данные объекта не будут потеряны.
Я не знаю, насколько это работоспособное решение, но, возможно, оно, по крайней мере, даст вам некоторые интересные идеи. ;)