10

我有两个新型 MATLAB 类 - B& C,它们都是抽象父类的具体子类,A. Ahgsetset(句柄类)的子类。我想将它们放在 MATLAB 中的一个数组中,并将它们都视为As. 它们大致定义为:

classdef A <hgsetget
methods
    function foo(this)
        %does some common stuff, then
        this.fooWorker;
    end
end %public Methods
methods(Abstract, Access=protected)
    fooWorker(this);
end %abstract Methods;
end

classdef B < A
methods(Access=protected)
    function fooWorker(this)
        %implementation
    end
end %protected Methods;
end

但是,如果我这样做:

arr = [b c]; % where b & c are objects of type B & C respectively.
arr(1).foo;
arr(2).foo;

MATLAB 会告诉BA两者foo都是b.

但是,如果我颠倒顺序:

arr = [c b];

它告诉我两者都是 type C,如果我尝试在两者上执行 foo ,它会执行,本质上是c.

任何想法如何以多态方式使用子类?

我知道我可以将它们放在一个单元阵列中并获得我需要的 90%。但这有点杂乱无章。

4

2 回答 2

17

您现在可以在 R2011a 中通过子类化 matlab.mixin.Heterogeneous 来执行此操作。因此,例如,在您的代码中,抽象类将是:

classdef A < matlab.mixin.Heterogeneous
methods
    function foo(this)
        disp('In abstract parent');
        this.fooWorker;
    end
end
methods(Abstract, Access=protected)
    fooWorker(this);
end
end

子类看起来像:

classdef B < A
methods(Access=protected)
    function fooWorker(this)
        disp('In B');
    end
end
end

同样对于“C”类。然后从 MATLAB 中给出以下输出:

>> b = B;
>> c = C;
>> arr = [b, c];
>> arr(1).foo
In abstract parent
In B
>> arr(2).foo
In abstract parent
In C
>> 
于 2011-04-22T14:27:53.903 回答
5

不幸的是,MATLAB 中数组的所有元素都必须属于同一类型。当您连接不同的类时,MATLAB 会尝试将它们全部转换为同一个类。

如果您已将您的一个类定义为低于或优于另一个(使用InferiorClasses属性或INFERIORTO / SUPERIORTO函数),则调用更高级类的方法。如果您没有指定类之间的关系,则这两个对象具有相同的优先级,并且 MATLAB 调用最左边的对象方法。这可能就是为什么arr = [b c];创建一个 B 类数组并arr = [c b];创建一个 C 类数组的原因。

选项 1:元胞数组

如果要在 object 上执行foo为类 B 定义的方法b,并在 object 上执行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完全相同,反之亦然):BC

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如上)中转换回来时,不会丢失任何对象数据。

我不知道这是一个多么可行的解决方案,但也许它至少会给你一些有趣的想法。;)

于 2009-09-22T05:38:20.350 回答