Skip to Main Content Skip to Search
Product Documentation

Subclassing MATLAB Built-In Classes

MATLAB Built-In Classes

Built-in classes represent fundamental kinds of data such as numeric arrays, logical arrays, and character arrays. Other built-in classes combine data belonging to these fundamental classes. For example, cell and struct arrays contain instances of fundamental classes.

Built-in classes define methods that perform operations on objects of these classes. For example, you can perform operations on numeric arrays, such as, sorting, rounding values, and element-wise and matrix multiplication. You can create an object of class double using an assignment statement, indexing expressions, or using converter functions.

See Classes (Data Types) for more information on MATLAB built-in classes.

Why Subclass Built-In Classes

Subclass a built-in class to extend the operations that you can perform on a particular class of data. For example, when you want to:

See Built-In Classes You Cannot Subclass for a list of which MATLAB built-in classes you can subclass.

Which Functions Work With Subclasses of Built-Ins

Consider a class that defines enumerations. It can derive from an integer class and inherit methods that enable you to compare and sort values. For example, integer classes like int32 support all the relational methods (eq, ge, gt, le, lt, ne).

To see a list of functions that the subclass has inherited as methods, use the methods function:

methods('SubclassName')

Generally, you can use an object of the subclass with any of the inherited methods and any functions coded in MATLAB that normally accept input arguments of the same class as the superclass.

See Behavior of Built-In Functions with Subclass Objects for information on other required methods.

Built-In Classes You Cannot Subclass

You cannot subclass the following built-in MATLAB classes:

Examples of Subclasses of Built-In Classes

Example — A Class to Manage uint8 Data

Example — Adding Properties to a Built-In Subclass

Example — A Class to Represent Hardware

Behavior of Built-In Functions with Subclass Objects

When you define a subclass of a built-in class, the subclass inherits all built-in class methods. In addition, MATLAB provide a number of built-in functions as subclass methods. However, built-in functions that work on built-in classes behave differently with subclasses, depending on which function you are using and whether your subclass defines properties.

Behavior Categories

When you call an inherited method on a subclass of a built-in class, the result of that call depends on the nature of the operation performed by the method. The behaviors of these methods fit into several categories.

To list the built-in functions that work with a subclass of a built-in class, use the methods function.

Subclasses That Define Properties

When a subclass of a built-in class defines properties, MATLAB no longer provides support for indexing and concatenation operations. MATLAB cannot use the built-in functions normally called for these operations because subclass properties can contain any data. The subclass must define what indexing and concatenation mean for a class with properties. If your subclass needs indexing and concatenation functionality, then the subclass must implement the appropriate methods.

The sections that follow list the methods you must implement in the subclass to support indexing and concatenation. Also, the section Example — Adding Properties to a Built-In Subclass provides an example of these methods.

Methods for Concatenation.  To support concatenation, the subclass must implement the following methods:

Concatenation Functions

Methods for Indexing.  To support indexing operations, the subclass must implement these methods:

Indexing Methods

More information on Built-In Methods

The following sections describe how different categories of methods behave with subclasses:

Extending the Operations of a Built-In Class

The MATLAB built-in class double defines a wide range of methods to perform arithmetic operations, indexing, matrix operation, and so on. Therefore, subclassing double enables you to add specific features without implementing many of the methods that a numeric class requires to function effectively in the MATLAB language.

The following class definition subclasses the built-in class double.

classdef DocSimpleDouble < double
   methods
      function obj = DocSimpleDouble(data)
         if nargin == 0
            data = 0;
         end
         obj = obj@double(data); % initialize the base class portion
      end
   end
end

You can create an instance of the class DocSimpleDouble and call any methods of the double class.

sc = DocSimpleDouble(1:10);
sc = 
  DocSimpleDouble
  double data:
     1     2     3     4     5     6     7     8     9    10
  Methods, Superclasses

Calling a method inherited from class double that operates on the data, like sum, returns a double and, therefore, uses the display method of class double:

sum(sc)
ans =
    55

You can index sc like an array of doubles. The returned value is the class of the subclass, not double:

a = sc(2:4)
a = 
  DocSimpleDouble
  double data:
     2     3     4
  Methods, Superclasses

Indexed assignment also works:

sc(1:5) = 5:-1:1
sc = 
  DocSimpleDouble
  double data:
     5     4     3     2     1     6     7     8     9    10
  Methods, Superclasses

Calling a method that modifies the order of the data elements operates on the data, but returns an object of the subclass:

sc = DocSimpleDouble(1:10);
sc(1:5) = 5:-1:1;
a = sort(sc)
a = 
  DocSimpleDouble
  double data:
     1     2     3     4     5     6     7     8     9    10
  Methods, Superclasses

Extending the Subclass.  You can extend the DocSimpleDouble with specialized methods to provide custom behavior. For example, see Example — A Class to Manage uint8 Data.

Built-In Methods That Operate on Data Values

Most built-in functions used with built-in classes are actually methods of the built-in class. For example, the double and single classes both have a sin method. All of these built-in class methods work with subclasses of the built-in class.

When you call a built-in method on a subclass object, MATLAB uses the superclass part of the subclass object as inputs to the method, and the value returned is same class as the built-in class. For example:

sc = DocSimpleDouble(1:10);
a = sin(sc)
class(a)

ans =

double

Built-In Methods That Operate on Data Organization

This group of built-in methods reorders or reshapes the input argument array. These methods operate on the superclass part of the subclass object, but return an object of the same type as the subclass. Methods in this group include:

Indexing Methods

Built-in classes use specially implemented versions of the subsref, subsasgn, and subsindex methods to implement indexing (subscripted reference and assignment). When you index a subclass object, only the built-in data is referenced (not the properties defined by your subclass). For example, indexing element 2 in the DocSimpleDouble subclass object returns the second element in the vector:

sc = DocSimpleDouble(1:10);
a = sc(2)
a = 
  DocSimpleDouble
  double data:
     2
  Methods, Superclasses

The value returned from an indexing operation is an object of the subclass. You cannot make subscripted references if your subclass defines properties unless your subclass overrides the default subsref method.

Assigning a new value to the second element in the DocSimpleDouble object operates only on the superclass data:

sc(2) = 12
sc = 
  DocSimpleDouble
  double data:
     1    12     3     4     5     6     7     8     9    10
  Methods, Superclasses

The subsref method also implements dot notation for methods. See Example — Adding Properties to a Built-In Subclass for an example of a subsref method.

Concatenation Functions

Built-in classes use the functions horzcat, vertcat, and cat to implement concatenation. When you use these functions with subclass objects of the same type, MATLAB concatenates the superclass data to form a new object. For example, you can concatenate objects of the DocSimpleDouble class:

sc1 = DocSimpleDouble(1:10);
sc2 = DocSimpleDouble(11:20);
[sc1 sc2]
ans = 
  DocSimpleDouble
  double data:
  Columns 1 through 13
     1     2     3     4     5     6     7     8     9    10    11    12    13
  Columns 14 through 20
    14    15    16    17    18    19    20
  Methods, Superclasses
[sc1; sc2]
ans = 
  DocSimpleDouble
  double data:
     1     2     3     4     5     6     7     8     9    10
    11    12    13    14    15    16    17    18    19    20
  Methods, Superclasses

Concatenate two objects along a third dimension:

c = cat(3,sc1,sc2)
c = cat(3,sc1,sc2)
c = 
  DocSimpleDouble
  double data:
(:,:,1) =
     1     2     3     4     5     6     7     8     9    10
(:,:,2) =
    11    12    13    14    15    16    17    18    19    20
  Methods, Superclasses

If the subclass of built-in class defines properties, you cannot concatenate objects of the subclass. Such an operation does not make sense because there is no way to know how to combine properties of different objects. However, your subclass can define custom horzcat and vertcat methods to support concatenation in whatever way makes sense for your subclass. See Concatenating DocExtendDouble Objects for an example.

Example — A Class to Manage uint8 Data

This example shows a class derived from the built-in uint8 class. This class simplifies the process of maintaining a collection of intensity image data defined by uint8 values. The basic operations of the class include:

The class data are matrices of intensity image data stored in the superclass part of the subclass object. This approach requires no properties.

The DocUint8 class stores the image data, which converts the data, if necessary:

classdef DocUint8 < uint8
   methods
      function obj = DocUint8(data)
      % Support no argument case
         if nargin == 0
            data = uint8(0);
         % If image data is not uint8, convert to uint8
         elseif ~strcmp('uint8',class(data))
            switch class(data)
               case 'uint16'
                  t = double(data)/65535;
                  data = uint8(round(t*255));
               case 'double'
                  data = uint8(round(data*255));
               otherwise
                  error('Not a supported image class')
            end
         end
         % assign data to superclass part of object
         obj = obj@uint8(data);
      end
      % Get uint8 data and setup call to imagesc
      function h = showImage(obj)
         data = uint8(obj);
         figure; colormap(gray(256))
         h = imagesc(data,[0 255]);
         axis image
         brighten(.2)
      end
   end
end

Using the DocUint8 Class

The DocUint8 class contains its own conversion code and provides a method to display all images stored as DocUint8 objects in a consistent way. For example:

cir = imread('circuit.tif');
img1 = DocUint8(cir);
img1.showImage;

Because DocUint8 subclasses uint8, you can use any of its methods. For example,

size(img1)
ans =
   280   272

returns the size of the image data.

Indexing Operations

Inherited methods perform indexing operations, but return objects of the same class as the subclass.

Therefore, you can index into the image data and call a subclass method:

showImage(img1(100:200,1:160));

Subscripted reference operations (controlled by the inherited subsref method) return a DocUint8 object.

You can assign values to indexed elements:

img1(100:120,140:160) = 255;
img1.showImage;

Subscripted assignment operations (controlled by the inherited subsasgn method) return a DocUint8 object.

Concatenation Operations

Concatenation operations work on DocUint8 objects because this class inherits the uint8 horzcat and vertcat methods, which return a DocUint8 object:

showImage([img1 img1]);

Data Operations

Methods that operate on data values, such as arithmetic operators, always return an object of the built-in type (not of the subclass type). For example, multiplying DocUint8 objects returns a uint8 object:

showImage(img1.*.8);
??? Undefined function or method 'showImage' for input arguments of type 'uint8'.

If you must be able to perform operations of this type, implement a subclass method to override the inherited method. The times method implements array (element-by-element) multiplication. See Implementing Operators for Your Class for a list of operator method names.

For example:

function o = times(obj,val)
   u8 = uint8(obj).*val; % Call uint8 times method
   o = DocUint8(u8);
end

Keep in mind that when you override a uint8 method, MATLAB calls the subclass method and no longer dispatches to the base class method. Therefore, explicitly call the uint8 times method or an infinite recursion can occur. Make the explicit call in this statement of the DocUint8 times method:

u8 = uint8(obj).*val;

After adding the times method to DocUint8, you can use the showImage method in expressions like:

showImage(img1.*1.8);

Example — Adding Properties to a Built-In Subclass

When your subclass defines properties, indexing and concatenation do not work by default. There is really no way for the default subsref, horzcat, and vertcat methods to work with unknown property types and values. The following example subclasses the double class and defines a single property intended to contain a descriptive character string.

Methods Implemented

The following methods modify the behavior of the DocExtendDouble class:

Property Added

The DocExtendDouble class defines the DataString property to contain text that describes the data contained in instances of the DocExtendDouble class. Keep in mind that the superclass part (double) of the class contains the data.

Subclass with Properties

The DocExtendDouble class extends double and implements methods to support subscripted reference and concatenation.

classdef DocExtendDouble < double
   
   properties
      DataString
   end
   
   methods
      function obj = DocExtendDouble(data,str)
      % Support calling with zero arguments but do not return empty object
         if nargin == 0
            data = 0;
            str = '';
         elseif nargin == 1
            str = '';
         end
         obj = obj@double(data);
         obj.DataString = str;
      end
     
      function sref = subsref(obj,s)
         % Implements dot notation for DataString and Data
         % as well as indexed reference
         switch s(1).type
            case '.'
               switch s(1).subs
                  case 'DataString'
                     sref = obj.DataString;
                  case 'Data'
                     sref = double(obj);
                     if length(s)>1 && strcmp(s(2).type, '()')
                        sref = subsref(sref,s(2:end));
                     end
               end
            case '()'
               sf = double(obj);
               if ~isempty(s(1).subs)
                  sf = subsref(sf,s(1:end));
               else
                  error('Not a supported subscripted reference')
               end
               sref = DocExtendDouble(sf,obj.DataString);
         end
      end
     
      function newobj = horzcat(varargin)
         % Horizontal concatenation - cellfun calls double
         % on all object to get superclass part. cellfun call local char
         % to get DataString and the creates new object that combines
         % doubles in vector and chars in cell array and creates new object
         d1 = cellfun(@double,varargin,'UniformOutput',false );
         data = horzcat(d1{:});
         str = horzcat(cellfun(@char,varargin,'UniformOutput',false));
         newobj = DocExtendDouble(data,str);
      end
      
      function newobj = vertcat(varargin)
         % Need both horzcat and vertcat
        d1 = cellfun(@double,varargin,'UniformOutput',false );
        data = vertcat(d1{:});
        str = vertcat(cellfun(@char,varargin,'UniformOutput',false));
        newobj = DocExtendDouble(data,str);
      end

      function str = char(obj)
         % Used for cat functions to return DataString
         str = obj.DataString;
      end

      function disp(obj)
         % Change the default display
         disp(obj.DataString)
         disp(double(obj))
      end
   end
end

Create an instance of DocExtendDouble and notice that the display is different from the default:

ed = DocExtendDouble(1:10,'One to ten')
ed = 
One to ten
     1     2     3     4     5     6     7     8     9    10

The sum function continues to operate on the superclass part of the object:

sum(ed)
ans =
    55

Subscripted reference works because the class implements a subsref method:

ed(10:-1:1)

ans = 

One to ten
    10     9     8     7     6     5     4     3     2     1

However, subscripted assignment does not work because the class does not define a subsasgn method:

ed(1:5) = 5:-1:1
Error using DocExtendDouble/subsasgn
Cannot use '(' or '{' to index into an object of class 'DocExtendDouble' because
'DocExtendDouble' defines properties and subclasses 'double'.
Click here for more information.

The sort function works on the superclass part of the object:

sort(ed)
ans = 
One to ten
     1     2     3     4     5     6     7     8     9    10

Indexed Reference of a DocExtendDouble Object

Subscripted reference (performed by subsref) requires the subclass to implement its own subsref method.

ed = DocExtendDouble(1:10,'One to ten');
a = ed(2)
a = 
One to ten
     2
whos
  Name      Size            Bytes  Class              Attributes
  a         1x1               132  DocExtendDouble              
  ed        1x10              204  DocExtendDouble

You can access the property data:

c = ed.DataString
c =
One to ten
whos
  Name      Size            Bytes  Class         
  c         1x10               20  char           
  ed        1x10              204  DocExtendDouble 

Concatenating DocExtendDouble Objects

Given the following two objects:

ed1 = DocExtendDouble([1:10],'One to ten');
ed2 = DocExtendDouble([10:-1:1],'Ten to one');

You can concatenate these objects along the horizontal dimension:

hcat = [ed1 ed2]
hcat = 
    'One to ten'    'Ten to one'
  Columns 1 through 13
     1     2     3     4     5     6     7     8     9    10    10     9     8
  Columns 14 through 20
     7     6     5     4     3     2     1
whos
  Name      Size            Bytes  Class           
  ed1       1x10              204  DocExtendDouble 
  ed2       1x10              204  DocExtendDouble
  hcat      1x20              528  DocExtendDouble  

Vertical concatenation works in a similar way:

vcat = [ed1;ed2]
vcat = 
    'One to ten'    'Ten to one'
     1     2     3     4     5     6     7     8     9    10
    10     9     8     7     6     5     4     3     2     1

Both horzcat and vertcat return a new object of the same class as the subclass.

Understanding size and numel

The size function returns the dimensions of an array. The numel function returns the number of elements in an array.

The default size and numel functions behave consistently with user-defined classes (see Classes Not Derived from Built-In Classes). Other MATLAB functions use size and numel to perform their operations and you usually do not need to overload them.

When used with subclasses of built-in classes, the size and numel functions behave the same as in the superclasses.

Consider the built-in class double:

d = 1:10;
size(d)

ans =

     1    10

numel(d)

ans =

    10

dsubref = d(7:end);

whos dsub
  Name      Size            Bytes  Class     Attributes

  dsubref   1x4                32  double

The double class defines these behaviors, including parentheses indexing.

Subclass Inherited Behavior

Classes behave like their superclasses, unless the subclass explicitly overrides any given behavior. For example, DocSimpleDouble subclasses double, but defines no properties:

classdef DocSimpleDouble < double
   methods     
      function obj = DocSimpleDouble(data)
         if nargin == 0
            data = 0;
         end
         obj = obj@double(data);
      end
   end
end

Create an object and assign to the superclass part of the object the values 1:10:

sd = DocSimpleDouble(1:10);

The size function returns the size of the superclass part:

size(sd)

ans =

     1    10

The numel function returns the number of elements in the superclass part:

numel(sd)

ans =

     10

Object arrays return the size of the built-in arrays also:

size([sd;sd])

ans =

     2    10

numel([sd;sd])

ans =

     20

The DocSimpleDouble class inherits the indexing behavior of the double class:

sdsubref = sd(7:end);
whos sdsubref
  Name       Size            Bytes  Class              Attributes

  sdsubref   1x4                88  DocSimpleDouble  

Classes Not Derived from Built-In Classes

Consider a simple value class. It does not inherit the array-like behaviors of the double class. For example:

classdef VerySimpleClass
   properties
      Value
   end
end

Create an instance of this class and assign a ten-element vector to the Value property:

vs = VerySimpleClass;
vs.Value = 1:10;
size(vs)

ans =

     1     1

numel(vs)

ans =

     1

 size([vs;vs])

ans =

     2     1

numel([vs;vs])

ans =

     2

vs is a scalar object, as opposed to an array of VerySimpleClass objects. The Value property is an array of doubles:

size(vs.Value)

ans =

     1    10

Apply indexing expressions to the object property:

vssubref = vs.Value(7:end);
whos vssubref
  Name       Size            Bytes  Class     Attributes

  vssubref   1x4                32  double   

vs.Value is an array of class double:

class(vs.Value)

ans =

double

Creating an array of VerySimpleClass objects

vsArray(1:10) = VerySimpleClass;

MATLAB does not apply scalar expansion to object array property value assignment. Use the deal function for this purpose:

[vsArray.Value] = deal(1:10);

Indexing rules for object arrays are equivalent to those of struct arrays:

v1 = vsArray(1).Value;
>> whos v1
  Name      Size            Bytes  Class     Attributes

  v1        1x10               80  double

vsArray(1).Value(6)

ans =

     6

Changing the Behavior of size

Subclasses of built-in numeric classes inherit a size method, which operates on the superclass part of the subclass object (this method is hidden). If you want size to behave in another way, you can override it by defining your own size method in your subclass.

Keep in mind that other MATLAB functions use the values returned by size. If you change the way size behaves, ensure that the values returned make sense for the intended use of your class.

Avoid Overloading numel

It is important to understand the significance of numel with respect to indexing. MATLAB calls numel to determine the number of elements returned by an indexed expression like:

A(index1,index2,...,indexn)

Both subsref and subsasgn use numel:

Subclasses of built-in classes always return scalar objects as a result of subscripted reference and always use scalar objects for subscripted assignment. The numel function returns the correct value for these operations and there is, therefore, no reason to overload numel.

If you define a class in which nargout for subsref or nargin for subsasgn is different from the value returned by the default numel, then overload numel for that class to ensure that it returns the correct values.

Example — A Class to Represent Hardware

This example shows the implementation of a class to represent an optical multiplex card. These cards typically have a number of input ports, which this class represents by the port data rates and names. There is also an output port. The output rate of a multiplex card is the sum of the input port data rates.

The DocMuxCard class defines the output rate as a Dependent property, and then defines a get access method for this property. The get.OutPutRate method calculates the actual output rate whenever the OutPutRate property is queried. See Property Get Methods for more information on this technique.

Why Derive from int32

The DocMuxCard class derives from the int32 class because 32–bit integers represent the input port data rates. The DocMuxCard class inherits the methods of the int32 class, which simplifies the implementation of this subclass.

Class Definition

Here is the definition of the DocMuxCard class. Notice that the input port rates initialize the int32 portion of class.

classdef DocMuxCard < int32
   properties
      InPutNames % cell array of strings
      OutPutName % a string
   end
   properties (Dependent = true)
      OutPutRate
   end
   methods
      function obj = DocMuxCard(inptnames, inptrates, outpname)
         obj = obj@int32(inptrates); % initial the int32 class portion
         obj.InPutNames = inptnames;
         obj.OutPutName = outpname;
      end
      function x = get.OutPutRate(obj)
         x = sum(obj); % calculate the value of the property
      end
      function x = subsref(card, s)
           if strcmp(s(1).type,'.')
              base = subsref@int32(card, s(1));
              if isscalar(s)
                 x = base;
              else
                 x = subsref(base, s(2:end));
              end
           else
              x = subsref(int32(card), s);
           end
        end
   end
end

Using the Class with Methods of int32

The constructor takes three arguments:

>> omx = DocMuxCard({'inp1','inp2','inp3','inp4'},[3 12 12 48],'outp')
omx = 
  DocMuxCard
  Properties:
    InPutNames: {'inp1'  'inp2'  'inp3'  'inp4'}
    OutPutName: 'outp'
    OutPutRate: 75
  int32 data:
           3          12          12          48
  Methods, Superclasses

You can treat an DocMuxCard object like an int32. For example, this statement accesses the int32 data in the object to determine the names of the input ports that have a rate of 12:

>> omx.InPutNames(omx==12)
ans = 
    'inp2'    'inp3'

Indexing the DocMuxCard object accesses the int32 vector of input port rates:

>> omx(1:2)
ans =
           3          12

The OupPutRate property get access method uses sum to sum the output port rates:

>> omx.OutPutRate
ans =
    75
  


Recommended Products

Includes the most popular MATLAB recorded presentations with Q&A sessions led by MATLAB experts.

 © 1984-2012- The MathWorks, Inc.    -   Site Help   -   Patents   -   Trademarks   -   Privacy Policy   -   Preventing Piracy   -   RSS