How do I create an array of abstract class objects

2019-02-03 07:03发布

问题:

As an example, suppose I have created an abstract class called Shape and two subclasses called Circle and Rectangle that both implement an (abstract) method called Draw. I would like to be able to create a number of Circle and Rectangle objects, store them in an array and call Draw on each array object by iterating through the array.

I have tried something like the following:

Shape.m:

classdef (Abstract) Shape < handle

    methods (Abstract)
        Draw(obj);
    end

end

Circle.m:

classdef Circle < Shape

    methods
        function obj = Draw(obj)
            disp('This is a circle');
        end
    end

end

Rectangle.m:

classdef Rectangle < Shape

    methods
        function obj = Draw(obj)
            disp('This is a rectangle');
        end
    end

end

test.m:

shapes = Shape.empty();

myrect = Rectangle();
mycirc = Circle();

shapes(end + 1) = myrect;
shapes(end + 1) = mycirc;

for i = 1:size(shapes,1)
    shapes(i).Draw();
end

When I try to run test.m, I get the following error message:

Error using Shape.empty
Abstract classes cannot be instantiated.
Class 'Shape' defines abstract methods
and/or properties.

Error in test (line 1)
shapes = Shape.empty();

回答1:

As is clear from the error, you cannot instantiate an abstract class (see sebastian's answer for details). However, there is a special superclass called matlab.mixin.Heterogeneous from which you can derive to allow creation of an array of different classes.

First, derive from matlab.mixin.Heterogeneous in Shape.m:

classdef (Abstract) Shape < handle & matlab.mixin.Heterogeneous

Then in your test script, initialize shapes from either Circle or Rectangle:

shapes = Circle.empty();

When you run the loop, the array will change class:

>> shapes

shapes = 

  1x2 heterogeneous Shape (Rectangle, Circle) array with no properties.

>> shapes(1)

ans = 

  Rectangle with no properties.

>> shapes(2)

ans = 

  Circle with no properties.

That should be all you need, but for additional control over a heterogeneous array, you can override the getDefaultScalarElement method of matlab.mixin.Heterogeneous to specify the default object. This should be overridden for abstract base classes:

Override this method if the Root Class is abstract or is not an appropriate default object for the classes in the heterogeneous hierarchy. getDefaultScalarElement must return an instance of another member of the heterogeneous hierarchy.

Say you want the default object to be Circle for an array of objects deriving from Shape:

methods (Static, Sealed, Access = protected)
    function default_object = getDefaultScalarElement
        default_object = Circle;
    end
end

Now missing elements in an array of objects derived from Shape will be filled with Circle objects:

>> clear r
>> r(2) = Rectangle
r = 
  1x2 heterogeneous Shape (Circle, Rectangle) array with no properties.
>> r(1)
ans = 
  Circle with no properties.
>> r(2)
ans = 
  Rectangle with no properties.


回答2:

From the docs:

abstract class — A class that cannot be instantiated, but that defines class components used by subclasses.

See: Mathworks-Docs

Which is, afaik, the definition of abstract classes in other programming languages as well (someone correct me if I'm wrong).

So to construct an array that holds various kinds of Shape elements, I'd guess you'll either have to make Shape non-abstract or implement another non-abstract class, that all your real implementations inherit from.

EDIT: For completeness:

I tried what you're trying achieve and at first sight, object-arrays with mixed elements that have a common superclass don't exist:

>> objects(1) = Foo();
>> objects(2) = FooBar();
The following error occurred converting from FooBar to Foo:
Error using Foo
Too many input arguments.

>> FooBar

ans = 

  FooBar handle with no properties.
  Methods, Events, Superclasses


Superclasses for class FooBar:

    Foo
    handle

EDIT 2: See chappjc's solution for this issue ;)