There are several MATLAB® functions that create arrays of
a specific size and type, such as ones
and zeros
. User-defined classes can add support
for array-creation functions without requiring the use of overloaded
method syntax.
Class support for any of the array-creation functions enables
you to develop code that you can share with built-in and user-defined
data types. For example, the class of the variable x
in
the following code can be a built-in type during initial development,
and then be replaced by a user-defined class that transparently overloads zeros
:
cls = class(x); zArray = zeros(m,n,cls);
Array-creation functions create arrays of a specific type in two ways:
Class name syntax — Specify class name that determines the type of array elements.
Prototype object syntax — Provide a prototype object that the function uses to determine the type and other characteristics of the array elements.
For example:
zArray = zeros(2,3,'uint8');
p = uint8([1 3 5 ; 2 4 6]);
zArray = zeros(2,3,'like',p);
After adding support for these functions to a class named MyClass
,
you can use similar syntax with that class:
zArray = zeros(2,3,'MyClass');
Or pass an object of your class:
p = MyClass(...);
zArray = zeros(size(p),'like',p);
MATLAB uses these arguments to dispatch to the appropriate method in your class.
The following functions support this kind of overloading.
To create an array of default objects, which require no input arguments for the constructor, then use the class name syntax.
To create an array of objects with specific property values or if the constructor needs other inputs, use the prototype object to provide this information.
Classes can support both the class name and the prototype object syntax.
You can implement a class name syntax with the true
and false
functions
even though these functions do not support that syntax by default.
If your class implements a class name syntax, but does not implement
a prototype object syntax for a particular function, you can still
call both syntaxes. For example, if you implement a static zeros
method
only, you can call:
zeros(...,'like',MyClass(...))
In the case in which you call the prototype object syntax, MATLAB first
searches for a method named zerosLike
. If MATLAB cannot
find this method, it calls for the zeros
static
method.
This feature is useful if you only need the class name to create the array. You do not need to implement both methods to support the complete array-creation function syntax. When you implement only the class name syntax, a call to a prototype object syntax is the same as the call to the class name syntax.
Use two separate methods to support an array-creation function. One method implements the class name syntax and the other implements the prototype object syntax.
For example, to support the zeros
function:
Implement the class name syntax:
zeros(...,'ClassName')
As a Static
method:
methods (Static) function z = zeros(varargin) ... end end
Implement the prototype object syntax:
zeros(...,'like',obj)
As a Hidden
method with the char
vector 'Like'
appended
to the name.
methods (Hidden) function z = zerosLike(obj,varargin) ... end end
The special support for array-creation functions results from the interpretation of the syntax.
A call to the zeros
function
of this form:
zeros(...,'ClassName
')
Calls the class static method with this syntax:
ClassName.zeros(varargin{1:end-1})
A call to the zeros
function
of this form:
zeros(...,'like',obj)
Calls the class method with this syntax:
zerosLike(obj,varargin{1:end-2})
The input arguments to an array-creation function can include the dimensions of the array the function returns and possibly other arguments. In general, there are three cases that your methods must support:
No dimension input arguments resulting in the return of a scalar. For example:
z = zeros('MyClass');
One or more dimensions equal to or less than zero, resulting in an empty array. For example:
z = zeros(2,0,'MyClass');
Any number of valid array dimensions specifying the size of the array. For example:
z = zeros(2,3,5,'MyClass');
When the array-creation function calls your class method, it
passes the input arguments, excluding the class name or the literal 'like'
and
the object variable to your method. You can implement your methods
with these signatures:
zeros(varargin)
for “class
name” methods
zeros(obj,varargin)
for “like
prototype object” methods
The Color
class represents a color in a specific color space,
such as, RGB
, HSV
, and so on. The discussions
in Class Name Method Implementations and Prototype Object Method Implementation use this class as a basis for the
overloaded method implementations.
classdef Color properties ColorValues = [0,0,0] ColorSpace = 'RGB' end methods function obj = Color(cSpace,values) if nargin > 0 obj.ColorSpace = cSpace; obj.ColorValues = values; end end end end
The zeros
function strips the final ClassName
char
vector
and uses it to form the call to the static method in the Color
class.
The arguments passed to the static method are the array dimension
arguments.
Here is an implementation of a zeros
method
for the Color
class. This implementation:
Defines the zeros
method as Static
(required)
Returns a scalar Color
object if
the call to zeros
has no dimension arguments
Returns an empty array if the call to zeros
has
any dimensions arguments equal to 0.
Returns an array of default Color
objects.
Use repmat
to create an array
of the dimensions specified by the call to zeros
.
classdef Color ... methods (Static) function z = zeros(varargin) if (nargin == 0) % For zeros('Color') z = Color; elseif any([varargin{:}] <= 0) % For zeros with any dimension <= 0 z = Color.empty(varargin{:}); else % For zeros(m,n,...,'Color') % Use property default values z = repmat(Color,varargin{:}); end end end end
The zeros
method uses default values for
the ColorValues
property because these values are
appropriate for this application. An implementation of a ones
method
can set the ColorValues
property to [1,1,1]
,
for example.
Suppose that you want to overload the randi
function
to achieve the following objectives:
Define each ColorValue
property
as a 1-by-3 array in the range of 1 to a specified maximum value (for
example, 1–255).
Accommodate scalar, empty, and multidimensional array sizes.
Return an array of Color
objects
of the specified dimensions, each with random ColorValues
.
classdef Color ... methods (Static) function r = randi(varargin) if (nargin == 0) % For randi('ClassName') r = Color('RGB',randi(255,[1,3])); elseif any([varargin{2:end}] <= 0) % For randi with any dimension <= 0 r = Color.empty(varargin{2:end}); else % For randi(max,m,n,...,'ClassName') if numel([varargin{:}]) < 2 error('Not enough input arguments') end dims = [varargin{2:end}]; r = zeros(dims,'Color'); for k = 1:prod(dims) r(k) = Color('RGB',randi(varargin{1},[1,3])); end end end end end
The objective of a method that returns an array of objects that
are “like a prototype object” depends on the requirements
of the class. For the Color
class, the zeroLike
method
creates objects that have the ColorSpace
property
value of the prototype object, but the ColorValues
are
all zero.
Here is an implementation of a zerosLike
method
for the Color
class. This implementation:
Defines the zerosLike
method as Hidden
Returns a scalar Color
object if
the call to the zeros
function has no dimension
arguments
Returns an empty array if the call to the zeros
function
has any dimension arguments that are negative or equal to 0.
Returns an array of Color
objects
of the dimensions specified by the call to the zeros
function.
classdef Color ... methods (Hidden) function z = zerosLike(obj,varargin) if nargin == 1 % For zeros('like',obj) cSpace = obj.ColorSpace; z = Color; z.ColorSpace = cSpace; elseif any([varargin{:}] <= 0) % For zeros with any dimension <= 0 z = Color.empty(varargin{:}); else % For zeros(m,n,...,'like',obj) if ~isscalar(obj) error('Prototype object must be scalar') end obj = Color(obj.ColorSpace,zeros(1,3,'like',obj.ColorValues)); z = repmat(obj,varargin{:}); end end end end
Here is the Color
class definition with the
overloaded methods.
In actual practice, the Color
class requires
error checking, color space conversions, and so on. This overly simplified
version illustrates the implementation of the overloaded methods.
classdef Color properties ColorValues = [0,0,0] ColorSpace = 'RGB' end methods function obj = Color(cSpace,values) if nargin > 0 obj.ColorSpace = cSpace; obj.ColorValues = values; end end end methods (Static) function z = zeros(varargin) if (nargin == 0) % For zeros('ClassName') z = Color; elseif any([varargin{:}] <= 0) % For zeros with any dimension <= 0 z = Color.empty(varargin{:}); else % For zeros(m,n,...,'ClassName') % Use property default values z = repmat(Color,varargin{:}); end end function r = randi(varargin) if (nargin == 0) % For randi('ClassName') r = Color('RGB',randi(255,[1,3])); elseif any([varargin{2:end}] <= 0) % For randi with any dimension <= 0 r = Color.empty(varargin{2:end}); else % For randi(max,m,n,...,'ClassName') if numel([varargin{:}]) < 2 error('Not enough input arguments') end dims = [varargin{2:end}]; r = zeros(dims,'Color'); for k = 1:prod(dims) r(k) = Color('RGB',randi(varargin{1},[1,3])); end end end end methods (Hidden) function z = zerosLike(obj,varargin) if nargin == 1 % For zeros('like',obj) cSpace = obj.ColorSpace; z = Color; z.ColorSpace = cSpace; elseif any([varargin{:}] <= 0) % For zeros with any dimension <= 0 z = Color.empty(varargin{:}); else % For zeros(m,n,...,'like',obj) if ~isscalar(obj) error('Prototype object must be scalar') end obj = Color(obj.ColorSpace,zeros(1,3,'like',obj.ColorValues)); z = repmat(obj,varargin{:}); end end end end